Python:在多个线程中从同一列表迭代器获取值是否安全



我知道生成器不是线程安全的。当两个线程同时访问时,会出现错误:

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来支持"线程安全…附加和弹出"。

最新更新