协程模块gevent
更新:导入一个有monkey.patch_all()的库,应该用函数把它包起来,不然会抛出错误
- RuntimeError: cannot release un-acquired lock
在《python高性能编程》一书中作者开始讲协程用的就是gevent模块作为例子。先举一个简单的例子:
协程极大大提高了性能
如果有100个foo,bar函数,每个需要阻塞一秒,那么串行执行需要100秒以上,而I/O多路复用执行,在一秒之后100个程序执行完毕。
协程提高性能的原因
- 所有的阻塞请求同时发生,只要函数输入到位
- cpu和操作系统的高度配合
-可以这么想象
1 线程读代码,读到一步需要去取数据,
2 线程自己不取数据,叫操作系统去取,取数据步骤参考途中1,3,4,2
3 操作系统取数据,线程就不读代码了,不干其他事,就在这等着
4 等数据到了,线程继续读代码,按代码干事
-重点来了,IO多路复用之后事情发生了变化:
- 线程很快找到查看了代码,发现代码中指出了有一些代码是可以同时执行的
- 比如下图有4处可以同时执行的代码,
-每一处代码里面可能也有阻塞,由于更里面的阻塞还不能发起请求主要先不关注,
- 线程拿起1处的代码,向系统发起请求,不等待,回到代码2处
- 再去操作系统处发送请求,如此反复,直到代码表明的特殊代码块都发起了请求
- 线程毫无停歇,只要操作系统使其得到数据,他就运行代码,并重复以上步骤
- 对比:在串行的情况下,线程在1.1处停下等待0.1s,而IO多路复用已经把活全做完了
协程作用的范围:
- 以下两个条件同时具备
I/O密集型任务。
任务本身结果不受其他任务
如何标记特殊代码块。(猴子补丁+gevent.spawn())
- 打上猴子补丁
from gevent import monkey
monkey.patch_all()
- gevent.spawn(function,*arg,**kwargs)
如果一个function内部没有阻塞,那么到这一步,正常执行
如果function能有阻塞,那么它被冻结并被标记成特殊代码块
- 使用内置方法使其开始执行
gevent.joinall()生成对象是list类型
iwait生成对象是生成器类型
对单个对象使用.join()方法,但是无法通过.value获取函数返回值
对单个对象使用.get()方法,可以直接运行并获得返回值,get(timeout=3)还能设置超时时间
- 不管哪个方法,生成对象的元素使用.value,得到那些特殊代码块的返回值,
如果没有返回值,那就不用管
信号量(from gevent.lock import BoundedSemaphore)
- 生成计算器对象,并设置值
bigest=BoundedSemaphore(5)
生成最大数为5的计数器
- 在代码中调用
使用with方法
或使用bigest.acquire(), bigest.release()
只能用一次的超时控制(gevent.Timeout):
- 在.join()方法前加上
timeout=Timeout(2)
timeout.start()
- timeout支持with调用,其__enter__()方法调用了start(),不知道在函数内部调用会有什么效果?
还有许多内容,但用好上面这些已能让你代码执行速度快上十倍了。
好吧,再来一点干货:不同线程内相互控制以及传递值
import gevent from gevent.event import AsyncResult
a = AsyncResult()
a.set('值')
a.get()='值'
解释:a实例化时给所有a.get() 后面的代码加锁,然后a.set()解锁,a.get()获取一个值