当前位置: 首页 > 工具软件 > greenlet > 使用案例 >

greenlet基础了解

屈晨
2023-12-01
  1. 简单介绍
     
    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()
    
  2. greenlet module和class
    getcurrent():返回当前的greenlet实例
    GreenletExit:是一个特殊的异常,当触发了这个异常的时候,即使不处理,也不会抛到其parent
    run:当greenlet启动的时候回调用到这个callable,如果我们需要继承greenlet.greenlet时,需要重写该方法
    switch:用于greenlet之间的切换
    parent:可读写属性
    dead:如果greenlet执行结束,那么该属性为true
    throw:切换到指定greenlet后立即抛出异常

  3. 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')
    
  4. 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()
    
  5. 防止内存泄露的方法

    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
    
 类似资料: