协程:Greenlet模块Gevent模块
Posted python-by-xiaoma
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了协程:Greenlet模块Gevent模块相关的知识,希望对你有一定的参考价值。
三、Greenlet模块
Greenlet是python的一个C扩展,来源于Stackless python,旨在提供可自行调度的‘微线程’, 即协程。generator实现的协程在yield value时只能将value返回给调用者(caller)。 而在greenlet中,target.switch(value)可以切换到指定的协程(target), 然后yield value。greenlet用switch来表示协程的切换,从一个协程切换到另一个协程需要显式指定。
安装 :pip3 install greenlet
from greenlet import greenlet def eat(name): print(‘%s eat 1‘ %name) g2.switch(‘egon‘) print(‘%s eat 2‘ %name) g2.switch() def play(name): print(‘%s play 1‘ %name) g1.switch() print(‘%s play 2‘ %name) g1=greenlet(eat) g2=greenlet(play) g1.switch(‘egon‘)#可以在第一次switch时传入参数,以后都不需要
有几个缺点
1.手动切换
2.不能规避I/O操作(睡眠)
gevent模块
安装:pip3 install gevent
Gevent 是一个第三方库,可以轻松通过gevent实现并发同步或异步编程,在gevent中用到的主要模式是Greenlet, 它是以C扩展模块形式接入Python的轻量级协程。 Greenlet全部运行在主程序操作系统进程的内部,但它们被协作式地调度。
g1=gevent.spawn(func,1,,2,3,x=4,y=5)创建一个协程对象g1,spawn括号内第一个参数是函数名,如eat,后面可以有多个参数,可以是位置实参或关键字实参,都是传给函数eat的 g2=gevent.spawn(func2) g1.join() #等待g1结束 g2.join() #等待g2结束 #或者上述两步合作一步:gevent.joinall([g1,g2]) g1.value#拿到func1的返回值 #例:遇到io主动切换 from gevent import monkey;monkey.patch_all() import gevent import time def eat(): print(‘eat food 1‘) time.sleep(2) print(‘eat food 2‘) def play(): print(‘play 1‘) time.sleep(1) print(‘play 2‘) g1=gevent.spawn(eat) g2=gevent.spawn(play) gevent.joinall([g1,g2]) print(‘主‘)
真正能实现协程的模块gevent import gevent def eat(): print(‘eating1‘) print(‘eating2‘) g1 = gevent.spawn(eat) #创建一个协程对象g1 #执行输出为空,表示它还没执行。 import gevent def eat(): print(‘eating1‘) print(‘eating2‘) g1 = gevent.spawn(eat) #创建一个协程对象g1 g1.join() #等待g1结束 #执行输出: #eating1 #eating2
geven 不能识别time.sleep() 需要用gevent.sleep() 或者导入一个模块monkey;monkey patch (猴子补丁)
#如果想让协程执行time.sleep()呢?由于默认,协程无法识别time.sleep()方法,需要导入一个模块monkey #monkey patch (猴子补丁) #用来在运行时动态修改已有的代码,而不需要修改原始代码。 from gevent import monkey;monkey.patch_all() # 它会把下面导入的所有的模块中的IO操作都打成一个包,gevent就能够认识这些IO了 import time import gevent def eat(): print(‘eating1‘) time.sleep(1) #延时调用 print(‘eating2‘) def play(): print(‘playing1‘) time.sleep(1) #延时调用 print(‘playing2‘) g1 = gevent.spawn(eat) #创建一个协程对象g1 g2 = gevent.spawn(play) g1.join() #等待g1结束 g2.join() #执行输出: eating1 playing1 eating2 playing2
结论:
使用gevent模块来执行多个函数,表示在这些函数遇到IO操作的时候可以在同一个线程中进行切换
利用其他任务的IO阻塞时间来切换到其他的任务继续执行
前提是:
spawn来发布协程任务
join负责开启并等待任务执行结束
gevent本身不认识其他模块中的IO操作,但是如果我们在导入其他模块之前执行from gevent import monkey;monkey.patch_all() 这行代码,必须在文件最开头
gevent就能够认识在这句话之后导入的模块中的所有IO操作了
Gevent之同步与异步
from gevent import spawn,joinall,monkey;monkey.patch_all() import time def task(pid): """ Some non-deterministic task """ time.sleep(0.5) print(‘Task %s done‘ % pid) def synchronous(): # 同步 for i in range(10): task(i) def asynchronous(): # 异步 g_l=[spawn(task,i) for i in range(10)] joinall(g_l) print(‘DONE‘) if __name__ == ‘__main__‘: print(‘Synchronous:‘) synchronous() print(‘Asynchronous:‘) asynchronous() # 上面程序的重要部分是将task函数封装到Greenlet内部线程的gevent.spawn。 # 初始化的greenlet列表存放在数组threads中,此数组被传给gevent.joinall 函数, # 后者阻塞当前流程,并执行所有给定的greenlet任务。执行流程只会在 所有greenlet执行完后才会继续向下走。
当一个任务执行时,依赖另外一个任务的结果时,这种情况不适合异步,只能用同步
Gevent之应用举例一
Gevent之应用举例二
以上是关于协程:Greenlet模块Gevent模块的主要内容,如果未能解决你的问题,请参考以下文章
并发编程 - 协程 - 1.协程概念/2.greenlet模块/3.gevent模块/4.gevent实现并发的套接字通信
# 进程/线程/协程 # IO:同步/异步/阻塞/非阻塞 # greenlet gevent # 事件驱动与异步IO # SelectPollEpoll异步IO 以及selectors模块 # (示