多线程:为什么生成器不是线程安全的?当它在线程之间共享时会发生什么?

Hai*_*ang 5 python multithreading generator

我正在阅读这个问题该问题询问生成器是否是线程安全的,一个答案说:

它不是线程安全的;同时调用可能会交错,并与局部变量混淆。

另一个答案表明您可以使用锁来确保一次只有一个线程使用生成器。

我是多线程的新手。谁能设计一个例子来说明当你使用没有锁的生成器时到底会发生什么?

例如,如果我这样做,它似乎没有任何问题:

import threading

def generator():
    for i in data:
        yield i

class CountThread(threading.Thread):
    def __init__(self, name):
        threading.Thread.__init__(self)
        self.name = name

    def run(self):
        for i in gen():
            print '{0} {1}'.format(self.name, i)

data = [i for i in xrange(100)]
gen = generator()
a = CountThread('a')
b = CountThread('b')
a.start()
b.start()
Run Code Online (Sandbox Code Playgroud)

Tho*_*zco 5

运行这个例子。

您将看到 10 000 个数字将在线程之间“共享”。您不会在两个线程中看到 10 000 个数字。

实际上,一个线程很可能会看到所有数字。

import threading

class CountThread(threading.Thread):
  def __init__(self, gen):
      threading.Thread.__init__(self)
      self.gen = gen
      self.numbers_seen = 0

  def run(self):
      for i in self.gen:
          self.numbers_seen += 1


def generator(data):
    for _ in data:
        yield data

gen = generator(xrange(10000))

a = CountThread(gen)
b = CountThread(gen)

a.start()
b.start()

a.join()
b.join()

print "Numbers seen in a", a.numbers_seen
print "Numbers seen in b", b.numbers_seen
Run Code Online (Sandbox Code Playgroud)

实际上,如果碰巧 Python 在执行期间切换线程(只需使用高于 10000 的值,例如 10000000),您将得到一个异常:

Exception in thread Thread-2:
Traceback (most recent call last):
  File "/usr/local/Cellar/python/2.7.5/Frameworks/Python.framework/Versions/2.7/lib/python2.7/threading.py", line 808, in __bootstrap_inner
    self.run()
  File "test.py", line 10, in run
    for i in self.gen:
ValueError: generator already executing
Run Code Online (Sandbox Code Playgroud)