首先,理想情况下,以下代码应同步运行3个greenlet,而不是异步运行所有3个greenlet。然而,奇怪的是,无论您拥有多少个greenlet,它都会为第二个greenlet启动一个额外的同步过程。我不是要问这个问题的解决方法,只是因为我想了解背后的原因。
import gevent
import time
def func(i):
t = time.time()
print('func %s started at %s' % (i, t))
secs = 5
statement = "after %s secs" % secs
gevent.sleep(secs)
print('func %s stopped in %s secs' % (i, (time.time() - t)))
apps = [gevent.Greenlet.spawn(func, i) for i in range(3)]
[app.run() for app in apps]
以下是示例标准输出:
func 0 started at 1491859273.2895772
func 1 started at 1491859273.2898045
func 2 started at 1491859273.2899446
func 0 stopped in 5.0095603466033936 secs
func 1 started at 1491859278.2993205
func 1 stopped in 5.0163233280181885 secs
func 2 stopped in 5.019707918167114 secs
func 1 stopped in 5.009198188781738 secs
func 1 started
如何发生两次?
答案 0 :(得分:0)
当您调用spawn()
时,已调度Greenlet进行调用。直接调用run()
不是公开记录的API的一部分,其效果未定义。然后,观察为什么当前行为的任何描述都需要超出所记录的API到实现细节。
使用gevent.joinall()
等待已安排的作业完成。
apps = [gevent.Greenlet.spawn(func, i) for i in range(3)]
gevent.joinall(apps)
正确收益:
func 0 started at 1491921603.6
func 1 started at 1491921603.6
func 2 started at 1491921603.6
func 0 stopped in 5.00121307373 secs
func 1 stopped in 5.00118613243 secs
func 2 stopped in 5.0011780262 secs
现在,让我们深入研究一下这种未定义的行为,并了解它很容易改变发布版本或系统到系统。
特别是Greenlet 1运行两次的原因是控制的主要线程首次合作产生的意外。您可以通过在第一次调用run()
之前显式地生成来更改此行为:
print "=== About to spawn greenlets..."
apps = [gevent.Greenlet.spawn(func, i) for i in range(3)]
print "=== All greenlets spawned; yielding..."
gevent.sleep(0)
print "=== Calling a duplicate run() invocation on each"
result = [app.run() for app in apps]
有了这个改变,它首先启动的是greenlet 0 - 你会注意到之前 <{strong> run()
是从来没有打过电话:
=== About to spawn greenlets...
=== All greenlets spawned; yielding...
func 0 started at 1491921486.57
func 1 started at 1491921486.57
func 2 started at 1491921486.57
=== Calling a duplicate run() invocation on each
func 0 started at 1491921486.57
func 0 stopped in 5.00335502625 secs
func 1 stopped in 5.00336790085 secs
func 2 stopped in 5.0033519268 secs
func 0 stopped in 5.0033428669 secs