我知道生成器不是线程安全的。当两个线程同时访问时,会出现错误:
ValueError:生成器已在执行
但迭代器不同于生成器,当在多个线程中使用时,不会引发异常,但会出现竞争条件。有关生成器和迭代器之间的比较,请参阅本文。
在那篇文章中,作者使用了一个例子,其中计数器被两个线程同时访问,导致其状态"self.i"被错误更新:
class Counter:
def __init__(self):
self.i = 0
def __iter__(self):
return self
def next(self):
self.i += 1
return self.i
因此,当线程停止时,self.i不会等于两个线程更新的次数,而是更小。
但是,当迭代器是列表迭代器时,会有竞争条件吗?例如:
import threading
class Counter(threading.Thread):
def __init__(self):
threading.Thread.__init__(self)
self.num_seen = 0
self.lock = threading.Lock()
def run(self):
global total
for x in idata:
self.num_seen += 1
print('{0} {1}'.format(self.name, self.num_seen))
with self.lock:
total += self.num_seen
count = 10000000
idata = iter([i for i in xrange(count)])
total = 0
nthreads = 5
threads = [Counter() for n in xrange(nthreads)]
for t in threads:
t.start()
for t in threads:
t.join()
print('total ' + str(total))
每次total等于count,这是否意味着迭代器是线程安全的?
我的猜测是,您所看到的内容取决于您所使用的Python的实现。我假设您使用的是CPython,看起来列表迭代器在CPython中可能是线程安全的。请注意,我没有说它们是线程安全的。仍然可能存在您的示例没有触发的边缘情况或竞争条件。因此,您不应该依赖它,因为它不是一个文档化的功能,因此它可能会在没有警告的情况下发生更改。
如果需要在多个线程中同时使用迭代器或生成器,则应该使用锁或互斥锁来保证线程安全。或者,正如@9000所建议的,使用一个有文档记录的deque来支持"线程安全…附加和弹出"。