这个问题让我把头发拉出来。
如果我做:
def mygen():
for i in range(100):
yield i
并从一千个线程中调用它,生成器如何知道每个线程接下来要发送什么?
每次我调用它时,生成器是否会保存一个带有计数器和调用者引用的表或类似的东西?
有点奇怪。
请澄清我的想法。
mygen
不必记住任何东西。每次调用都mygen()
返回一个独立的迭代。另一方面,这些可迭代对象具有状态:每次next()
调用一个时,它都会跳转到生成器代码中的正确位置——当yield
遇到 a 时,将控制权交还给调用者。实际实现相当混乱,但原则上您可以想象这样的迭代器存储局部变量、字节码和字节码中的当前位置(也称为指令指针)。这里的线程没有什么特别之处。
像这样的函数在调用时将返回一个生成器对象。如果您有单独的线程调用next()
同一个生成器对象,它们将相互干扰。也就是说,5个线程next()
每个调用10次会得到50个不同的yield。
如果两个线程各自通过在线程内调用来创建一个生成器mygen()
,那么它们将拥有单独的生成器对象。
生成器是一个对象,它的状态将存储在内存中,因此每个创建的两个线程mygen()
将引用不同的对象。这与从 a 创建对象的两个线程没有什么不同class
,它们每个都有不同的对象,即使类是相同的。
如果你是从 C 背景来的,这与static
带有变量的函数不同。状态保存在对象中,而不是静态保存在函数中包含的变量中。
如果你这样看可能会更清楚。代替:
for i in mygen():
. . .
采用:
gen_obj = mygen()
for i in gen_obj:
. . .
然后你可以看到 mygen() 只被调用了一次,它创建了一个新对象,并且被迭代的是那个对象。如果需要,您可以在同一个线程中创建两个序列:
gen1 = mygen()
gen2 = mygen()
print(gen1.__next__(), gen2.__next__(), gen1.__next__(), gen2.__next__())
这将打印 0、0、1、1。
如果愿意,您可以从两个线程访问相同的迭代器,只需将生成器对象存储在全局中:
global_gen = mygen()
线程 1:
for i in global_gen:
. . .
线程 2:
for i in global_gen:
. . .
这可能会造成各种破坏。:-)