1

我有从不同线程访问的单例。这个单例通过生成器提供数据。生成器应该被访问数据的线程完全消耗。每个访问数据的线程都应该使用一个的生成器。这是我的代码:

from   datetime import datetime
import threading
import time

class MySingletonCls:

    def get_data(self, repeat):
        self.nr = 0
        for x in xrange(repeat):
            time.sleep(0.001)
            self.nr += 1
            yield x

_my_singleton = None

def MySingleton():
    global _my_singleton
    if _my_singleton == None:
        _my_singleton = MySingletonCls()
    return _my_singleton

def test_singleton():
    def worker():
        singleton = MySingleton()
        cnt = 0
        for x in singleton.get_data(100):
            cnt += 1
        print singleton.nr, cnt
    threads = []
    num_worker_threads = 5
    for i in range(num_worker_threads):
        t = threading.Thread(target=worker)
        threads.append(t)
        t.start()
    for t in threads:
        t.join()

test_singleton()

我希望每个工人都收到 100 个条目,实际上就是这样。但是访问单例中的计数器给了我非常奇怪的数字。这是我的程序的输出:

457 100
468 100
470 100
471 100
475 100

这里发生了什么?为每个线程生成单例生成器的条目有多少?为什么单例计数器显示这个奇怪的值?我怎样才能使这个线程安全?

4

3 回答 3

1

MySingletonCls.get_data中,self总是指同一个对象,因此self.nr在每个线程中命名同一个对象槽。

这意味着singleton.nr每次线程启动时将其重置为 0,然后由每个线程并行递增。您看到打印的数字从 457 到 475,因为最后一个线程在其他线程通过其迭代总共为 25 时开始。

于 2012-07-08T21:00:04.440 回答
1

因为只有一个单例实例,所以该nr属性在所有生成器之间共享。即使每次调用都会创建一个新的生成器,它们都使用相同的nr属性。因此,每当任何线程消耗来自生成器的元素时,它都会增加nr,并且每当创建新的生成器时,它都会重置nr。这些增量和重置在线程中不可预测地发生。

如果您希望每次调用都get_data产生一个完全独立的生成器,则不能让它们都依赖于相同的nr属性。请注意,生成器将在每次 yield 时“暂停”并保留当时函数的状态,因此您根本不需要使用属性。你可以只使用一个局部变量:

def get_data(self, repeat):
    nr = 0
    for x in xrange(repeat):
        time.sleep(0.001)
        nr += 1
        yield x

但是,尚不清楚您要做什么self.nr,因为您没有在生成器中生成它。没有办法允许多个线程随意改变单例对象并仍然从所有线程中获得一致的结果。如果线程中的操作导致单例对象状态发生变化,那么您永远无法判断这些变化何时发生。

于 2012-07-08T21:00:21.270 回答
0

x您在打印时正在让步self.nr。每次产生一个新的worker,它被设置self.nr为0,然后从0开始计数到100。

所以,时不时地self.nr被重置,并且因为self在所有工作人员中都是相同的,所以它是递增的(num of workers)*(100)。所以,self.nr应该等于那个,考虑到它被重置的次数0。您进行计算,包括每次“重置”之间的时间。:D,但我想这是你的问题。

于 2012-07-08T21:00:13.937 回答