协程模块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()获取一个值 

  

原文地址:https://www.cnblogs.com/yuanji2018/p/10016758.html