单身生成器,多个消费者

时间:2012-07-08 20:51:23

标签: python thread-safety generator

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

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

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

3 个答案:

答案 0 :(得分:1)

MySingletonCls.get_data中,self始终引用同一个对象,因此self.nr在每个帖子中命名相同的对象槽。

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

答案 1 :(得分:1)

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

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

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

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

答案 2 :(得分:0)

在打印x时,您正在屈服self.nr。每次生成新工作程序时,它都会将self.nr设置为0,然后从0开始计数到100.

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