【问题标题】:Python: Is it safe to get value from the same list iterator in multiple threads?Python:在多个线程中从同一个列表迭代器中获取值是否安全?
【发布时间】:2023-03-15 02:43:01
【问题描述】:

我知道generators are not thread safe。两个线程同时访问时,出现错误:

ValueError: 生成器已经在执行

但迭代器与生成器不同,在多线程中使用时,不会引发异常,但会有竞态条件。生成器和迭代器的比较见this article。

在那篇文章中,作者举了一个例子,一个 Counter 被两个线程同时访问,导致它的状态 '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,这是否意味着迭代器是线程安全的?

【问题讨论】:

  • 我也注意到objects returned by iter() seem thread-safe。不幸的是,这不是一个记录在案的功能。
  • 顺便说一句,为什么您可能需要在多个线程中使用相同的迭代器?为什么不使用明确线程安全的deque?

标签: python multithreading


【解决方案1】:

我的猜测是,您所看到的取决于您使用的 Python 的实现。我假设您使用的是 CPython,并且看起来列表迭代器在 CPython 中可能是线程安全的。请注意,我没有说它们是线程安全的。仍然可能存在您的示例未触发的边缘情况或竞争条件。因此,您不应该依赖它,因为它不是文档中的功能,因此它可能会在没有警告的情况下更改。

如果您需要在多个线程中同时使用迭代器或生成器,那么您应该使用锁或互斥锁来保证线程安全。或者按照@9000 的建议,使用记录在案以支持“线程安全...追加和弹出”的双端队列。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-11-27
    • 2012-10-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-10-07
    相关资源
    最近更新 更多