简单介绍
Greenlet是python的一个C扩展,来源于Stackless python,旨在提供可自行调度的‘微线程’, 即协程。generator实现的协程在yield value时只能将value返回给调用者(caller)。 而在greenlet中,target.switch(value)可以切换到指定的协程(target), 然后yield value。greenlet用switch来表示协程的切换,从一个协程切换到另一个协程需要显式指定。
def test1():
print("l2")
gr2.switch()
print(34)
gr2.switch()
def test2():
print(56)
gr1.switch()
print(78)
gr1 = greenlet(test1)
gr2 = greenlet(test2)
gr1.switch()
greenlet module和class
getcurrent():返回当前的greenlet实例
GreenletExit:是一个特殊的异常,当触发了这个异常的时候,即使不处理,也不会抛到其parent
run:当greenlet启动的时候回调用到这个callable,如果我们需要继承greenlet.greenlet时,需要重写该方法
switch:用于greenlet之间的切换
parent:可读写属性
dead:如果greenlet执行结束,那么该属性为true
throw:切换到指定greenlet后立即抛出异常
switch
对于greenlet,最常用的写法是x=gr.switch(y)。其作用是切换到gr,且传入的参数为y。当从其他写成切换回来的时候,将值付给x
import greenlet
def test1(x, y):
z = gr2.switch(x+y)
print('test1 ', z)
def test2(u):
print('test2 ', u)
gr1.switch(10)
gr1 = greenlet.greenlet(test1)
gr2 = greenlet.greenlet(test2)
print(gr1.switch("hello", " world"))
每一个Greenlet都有一个parent,一个新的greenlet在哪里创生,当前环境的greenlet就是这个新greenlet的parent。所有的greenlet勾陈一棵树,其根节点就是没有手动创建的greenlet时候的"main"greenlet(在首次import greenlet的时候实例化)。当一个协程正常结束,执行流程回到其对应的parent;或者在一个协程中抛出未被捕获的异常,该异常也是传递到parent。
import greenlet
def test1(x,y):
print(id(greenlet.getcurrent()),id(greenlet.getcurrent().parent))
z = gr2.switch(x+y)
print('back z',z)
def test2(u):
print(id(greenlet.getcurrent()),id(greenlet.getcurrent().parent))
return "hehe"
gr1 = greenlet.greenlet(test1)
gr2 = greenlet.greenlet(test2)
print(id(greenlet.getcurrent()),id(gr1),id(gr2))
print(gr1.switch("hello","world"),"back to main")
尽管test1所在协程gr1切换到gr2,但gr2的parent还是“main”greenlet,因为默认parent取决于greenlet的创生环境。另外test2中的return之后整个返回值返回到了parent,而不是switch到该协程的地方。
import greenlet
def test1(x, y):
try:
z = gr2.switch(x+y)
except Exception:
print ('catch Exception in test1')
def test2(u):
assert False
gr1 = greenlet.greenlet(test1)
gr2 = greenlet.greenlet(test2)
try:
gr1.switch("hello", " world")
except:
print('catch Exception in main')
Greenlet Traceing
Greenlet也提供了接口使得程序员可以监控greenlet的整个调度流程,主要是geetrace和settrace(callback)函数。如下所示:
import greenlet
def test_greenlet_tracing():
def callback(event, args):
print event, 'from', id(args[0]), 'to', id(args[1])
def dummy():
g2.switch()
def dummyexception():
raise Exception('excep in coroutine')
main = greenlet.getcurrent()
g1 = greenlet.greenlet(dummy)
g2 = greenlet.greenlet(dummyexception)
print 'main id %s, gr1 id %s, gr2 id %s' % (id(main), id(g1), id(g2))
oldtrace = greenlet.settrace(callback)
try:
g1.switch()
except:
print 'Exception'
finally:
greenlet.settrace(oldtrace)
test_greenlet_tracing()
防止内存泄露的方法
from greenlet import greenlet, GreenletExit
huge = []
def show_leak():
def test1():
gr2.switch()
def test2():
huge.extend([x* x for x in range(100)])
try:
gr1.switch()
finally:
print 'finish switch del huge'
del huge[:]
gr1 = greenlet(test1)
gr2 = greenlet(test2)
gr1.switch()
gr1 = gr2 = None
print 'length of huge is zero ? %s' % len(huge)
if __name__ == '__main__':
show_leak()
# output :
# finish switch del huge
# length of huge is zero ? 0