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

时间:2013-11-19 19:02:50

标签: python multithreading

我知道generators are not thread safe。当两个线程同时访问时,会发生错误:

  

ValueError:生成器已在执行

但是迭代器与生成器不同,当在多个线程中使用时,不会引发异常,但会有竞争条件。有关生成器和迭代器之间的比较,请参阅this article

在那篇文章中,作者使用了一个例子,其中一个计数器同时由两个线程访问,导致其状态'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))

Everytime total等于count,这是否意味着迭代器是线程安全的?

1 个答案:

答案 0 :(得分:0)

我的猜测是,您所看到的内容取决于您正在使用的Python的实现。我假设您正在使用CPython,看起来列表迭代器在CPython中可能是线程安全的。请注意,我没有说他们线程安全的。您的示例可能仍会触发边缘情况或竞争条件。因此,您不应该依赖于此,因为它不是一个记录的功能,因此它可能会在没有警告的情况下发生变化。

如果需要在多个线程中同时使用迭代器或生成器,则应使用锁或互斥锁来保证线程安全。或者根据@ 9000的建议,使用deque来记录支持“线程安全...附加和弹出”。