gevent是第三方库,通过Greenlet提供协程支持(一个Greenlet对象就是一个协程),其基本思想是:
当一个greenlet遇到IO操作时,比如访问网络,就会切换执行其它greenlet,等待IO操作完成,再在适当的时候再切换回来继续执行。由于IO操作非常耗时,通过gevent自动切换greenlet,保证总有一个greenlet在执行,避免空等IO,提高执行效率。
1、Greenlet.join(timeout) 函数
与多进程/多线程中join用法基本一致,join函数使当前协程处于wait状态,直到调用join的协程结束。和多线程不同之处有2点:
(1)主协程若不调用类似join的函数使自己处于wait状态从而交出控制权,则子协程永远不会被调度执行(多线程的调度是由系统完成,协程则必须自己主动让出控制权,其他协程才能被调度执行)。
(2)主协程若不调用join等待子协程执行完退出,主协程执行完后,会自动杀死子协程,而不是等待子协程执行完毕。
2、gevent.monkey
将标准库中部分阻塞式调用替换为非阻塞式调用。gevent实现了协程的创建、调度,但每个greenlet内部调用的函数必须是非阻塞式的,如果使用了阻塞式函数,当前协程无法交出控制权实现协程切换。
下面代码中,time.sleep() 是阻塞式的,协程无法切换,因此协程按照启动的顺序依次执行;gevent.sleep() 是非阻塞式的,协程可以切换,各协程交替执行。
from gevent import monkey; monkey.patch_socket()
import gevent
import time
def f(n):
for i in range(n):
print gevent.getcurrent(), i
time.sleep(0.1)
# gevent.sleep(0.1)
g1 = gevent.spawn(f, 2)
g2 = gevent.spawn(f, 4)
g3 = gevent.spawn(f, 6)
g1.join()
g2.join()
g3.join()
# out:
<Greenlet at 0x3a05548L: f(2)> 0
<Greenlet at 0x3a05548L: f(2)> 1
<Greenlet at 0x3a05648L: f(4)> 0
<Greenlet at 0x3a05648L: f(4)> 1
<Greenlet at 0x3a05648L: f(4)> 2
<Greenlet at 0x3a05648L: f(4)> 3
<Greenlet at 0x3a05748L: f(6)> 0
<Greenlet at 0x3a05748L: f(6)> 1
<Greenlet at 0x3a05748L: f(6)> 2
<Greenlet at 0x3a05748L: f(6)> 3
<Greenlet at 0x3a05748L: f(6)> 4
<Greenlet at 0x3a05748L: f(6)> 5
from gevent import monkey; monkey.patch_socket()
import gevent
import time
def f(n):
for i in range(n):
print gevent.getcurrent(), i
# time.sleep(0.1)
gevent.sleep(0.1)
g1 = gevent.spawn(f, 2)
g2 = gevent.spawn(f, 4)
g3 = gevent.spawn(f, 6)
g1.join()
g2.join()
g3.join()
out:
<Greenlet at 0x4085548L: f(2)> 0
<Greenlet at 0x4085648L: f(4)> 0
<Greenlet at 0x4085748L: f(6)> 0
<Greenlet at 0x4085548L: f(2)> 1
<Greenlet at 0x4085648L: f(4)> 1
<Greenlet at 0x4085748L: f(6)> 1
<Greenlet at 0x4085648L: f(4)> 2
<Greenlet at 0x4085748L: f(6)> 2
<Greenlet at 0x4085648L: f(4)> 3
<Greenlet at 0x4085748L: f(6)> 3
<Greenlet at 0x4085748L: f(6)> 4
<Greenlet at 0x4085748L: f(6)> 5
monkey.patch_all() 将monkey实现的非阻塞函数全部完成替换。一般情况下,推荐使用patch_all全部替换,monkey.patch_all()应当尽早执行,最好是程序的第一行,以避免出现不可预期的情况。
3、gevent.pool.Group
通过Group对象,可以管理一组greenlet,当容器中的greenlet任务完成,容器会自动将其删去。