python并发编程gevent模块以及猴子补丁学习

1.gevent模块简介

         gevent是一个基于libev的并发库。它为各种并发和网络相关的任务提供了整洁的API

         gevent中用到的主要模式是greenlet它是以C扩展模块形式接入Python的轻量级协程。 greenlet全部运行在主程序操作系统进程的内部,但它们被协作式地调度。

 

2.gevent程序举例

例程一:

 

[python] view plain copy
 
  1. import gevent  
  2.    
  3. def foo():  
  4.     print('Running in foo')  
  5.     gevent.sleep(0)  
  6.     print('Explicit context switch to fooagain')  
  7.    
  8. def bar():  
  9.     print('Explicit context to bar')  
  10.     gevent.sleep(0)  
  11.     print('Implicit context switch back tobar')  
  12.    
  13. gevent.joinall([  
  14.     gevent.spawn(foo),  
  15.     gevent.spawn(bar),  
  16. ])  

例程二:

 

 

[python] view plain copy
 
  1. import gevent  
  2. import random  
  3.    
  4. def task(pid):  
  5.     """ 
  6.     Some non-deterministic task 
  7.     """  
  8.     gevent.sleep(random.randint(0,2)*0.001)  
  9.     print('Task %s done' % pid)  
  10.    
  11. def synchronous():  
  12.     for i in range(1,10):  
  13.         task(i)  
  14.    
  15. def asynchronous():  
  16.     threads = [gevent.spawn(task, i) for i inxrange(10)]  
  17.     gevent.joinall(threads)  
  18.    
  19. print('Synchronous:')  
  20. synchronous()  
  21.    
  22. print('Asynchronous:')  
  23. asynchronous()  

例程三: 

 

 

[python] view plain copy
 
  1. import time  
  2. import gevent  
  3. from gevent importselect  
  4.    
  5. start = time.time()  
  6. tic = lambda: 'at%1.1f seconds' % (time.time() - start)  
  7.    
  8. def gr1():  
  9.     # Busy waits for a second, but we don'twant to stick around...  
  10.     print('Started Polling: %s' % tic())  
  11.     select.select([], [], [], 2)  
  12.     print('Ended Polling: %s' % tic())  
  13.    
  14. def gr2():  
  15.     # Busy waits for a second, but we don'twant to stick around...  
  16.     print('Started Polling: %s' % tic())  
  17.     select.select([], [], [], 2)  
  18.     print('Ended Polling: %s' % tic())  
  19.    
  20. def gr3():  
  21.     print("Hey lets do some stuff whilethe greenlets poll, %s" % tic())  
  22.     gevent.sleep(1)  
  23.    
  24. gevent.joinall([  
  25.     gevent.spawn(gr1),  
  26.     gevent.spawn(gr2),  
  27.     gevent.spawn(gr3),  
  28. ])  

 

备注:

1)程序的重要部分是将任务函数封装到gevent.spawn。初始化的greenlet列表存放在数组threads中,此数组被传给gevent.joinall 函数,gevent.joinall阻塞当前流程,并执行所有给定的greenlet执行流程只会在所有greenlet执行完后才会继续向下走。

2gevent实现了python标准库里面大部分的阻塞式系统调用,包括socket、ssl、threading和select等模块,而将这些阻塞式调用变为协作式运行(参见猴子补丁部分)。

 

3.猴子补丁Monkey Patch

(1)猴子补丁的由来

 

         猴子补丁的这个叫法起源于Zope框架,大家在修正Zope的Bug的时候经常在程序后面追加更新部分,这些被称作是“杂牌军补丁(guerillapatch)”,后来guerilla就渐渐的写成了gorllia(猩猩),再后来就写了monkey(猴子),所以猴子补丁的叫法是这么莫名其妙的得来的。

         后来在动态语言中,不改变源代码而对功能进行追加和变更,统称为“猴子补丁”。所以猴子补丁并不是Python中专有的。猴子补丁这种东西充分利用了动态语言的灵活性,可以对现有的语言Api进行追加,替换,修改Bug,甚至性能优化等等。

   使用猴子补丁的方式,gevent能够修改标准库里面大部分的阻塞式系统调用,包括socket、ssl、threading和 select等模块,而变为协作式运行。也就是通过猴子补丁的monkey.patch_xxx()来将python标准库中模块或函数改成gevent中的响应的具有协程的协作式对象。这样在不改变原有代码的情况下,将应用的阻塞式方法,变成协程式的。

(2)猴子补丁使用时的注意事项

   猴子补丁的功能很强大,但是也带来了很多的风险,尤其是像gevent这种直接进行API替换的补丁,整个Python进程所使用的模块都会被替换,可能自己的代码能hold住,但是其它第三方库,有时候问题并不好排查,即使排查出来也是很棘手,所以,就像松本建议的那样,如果要使用猴子补丁,那么只是做功能追加,尽量避免大规模的API覆盖。

  虽然猴子补丁仍然是邪恶的(evil),但在这种情况下它是“有用的邪恶(useful evil)”。

(3)python中使用gevent的猴子补丁的举例

  具体见下面的例子:

 

[python] view plain copy
 
  1. import socket  
  2. print(socket.socket)  
  3.    
  4. print("Aftermonkey patch")  
  5. from gevent importmonkey  
  6. monkey.patch_socket()  
  7. print(socket.socket)  
  8.    
  9. import select  
  10. print(select.select)  
  11. monkey.patch_select()  
  12. print("Aftermonkey patch")  
  13. print(select.select)  

程序输出如下:

[plain] view plain copy
 
  1. class 'socket.socket'  
  2. After monkey patch  
  3. class 'gevent.socket.socket'  
  4.   
  5. built-in function select  
  6. After monkey patch  
  7. function select at 0x1924de8  


学习资料参考于:

 

http://xlambda.com/gevent-tutorial/

posted @ 2017-08-21 18:12  天涯海角路  阅读(146)  评论(0)    收藏  举报