我有从不同线程访问的单例。这个单例是用生成器提供数据。访问数据的线程应该完全消耗生成器。访问数据的每个线程都应使用 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
这里发生了什么?每个线程生成单例生成器的条目数是多少?为什么单身人士计数器显示出这种奇怪的价值?我怎样才能使这个线程安全?
答案 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,但我猜这是你的问题。