многопоточность: почему генераторы не являются потокобезопасными? Что происходит, когда он разделяется между потоками?

Я читаю этот вопрос, который спрашивает, являются ли генераторы потокобезопасными, и один ответ сказал:

Это не поточно-безопасный; одновременные вызовы могут чередовать и помешать локальным переменным.

Другой ответ показывает, что вы можете использовать блокировку, чтобы гарантировать, что только один поток использует генератор за раз.

Я новичок в многопоточности. Может ли кто-нибудь разработать пример, чтобы показать, что именно происходит, когда вы используете генератор без блокировки?

Например, у меня нет никаких проблем, если я это сделаю:

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() 

Запустите этот пример.

Вы увидите, что 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 

Фактически, если произойдет, что 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