gevent
gevent是一个基于协程的python网络库,在遇到IO阻塞时,程序会自动进行切换,可以让我们用同步的方式写异步IO代码。
gevent是python的一个并发框架,以微线程greenlet为核心,使用了epoll事件监听机制以及诸多其他优化而变得高效。而且其中有个monkey类,将现有基于Python线程直接转化为greenlet(类似于打patch)。在运行时的具体流程大概就是:
当一个greenlet遇到IO操作时,比如访问网络/睡眠等待,就自动切换到其他的greenlet,等到IO操作完成,再在适当的时候切换回来继续执行。由于IO操作非常耗时,经常使程序处于等待状态,有了gevent为我们自动切换协程,就保证总有greenlet在运行,而不是等待IO。同时也因为只有一个线程在执行,会极大的减少上下文切换的成本。
gevent基本使用
# -*- coding: utf-8 -*- import gevent def f1(): for i in range(5): print 'run func: f1, index: %s ' % i gevent.sleep(0) def f2(): for i in range(5): print 'run func: f2, index: %s ' % i gevent.sleep(0) t1 = gevent.spawn(f1) t2 = gevent.spawn(f2) gevent.joinall([t1, t2])
运行后输出如下图所示:
由图中可以看出,f1和f2是交叉打印信息的,因为在代码执行的过程中,我们人为使用gevent.sleep(0)创建了一个阻塞,gevent在运行到这里时就会自动切换函数切换函数。也可以在执行的时候sleep更长时间,可以发现两个函数基本是同时运行然后各自等待。
在实际运用的过程中,我们如果有需要通过人为sleep来增加时间间隔或者确保部分逻辑安全的时候,此处使用就很方便了。当然,更多时候我们还是在需要进行网络请求的时候使用gevent:
# -*- coding: utf-8 -*- from gevent import monkey; monkey.patch_all() import gevent import requests from datetime import datetime def f(url): print 'time: %s, GET: %s' % (datetime.now(), url) resp = requests.get(url) print 'time: %s, %d bytes received from %s.' % ( datetime.now(), len(resp.text), url) gevent.joinall([ gevent.spawn(f, 'https://www.python.org/'), gevent.spawn(f, 'https://www.yahoo.com/'), gevent.spawn(f, 'https://github.com/'), ])
运行上述代码,结果如下:
由上图可以看出,程序基本在同一时间触发了对三个网站的请求,然后各自进行,分别结束。也就是当gevent发现阻塞之后,让当前急需执行,然后自动切换到了另外的请求中运行。
加锁
如果需要在使用gevent的时候加锁,也是非常方便的:
# -*- coding: utf-8 -*- import gevent from gevent.lock import Semaphore sem = Semaphore(1) def f1(): for i in range(5): sem.acquire() print 'run f1, this is ', i sem.release() gevent.sleep(1) def f2(): for i in range(5): sem.acquire() print 'run f2, that is ', i sem.release() gevent.sleep(0.3) t1 = gevent.spawn(f1) t2 = gevent.spawn(f2) gevent.joinall([t1, t2])
运行结果如下:
由输出可以发现,程序会同时判断是否在sleep以及是否有锁两种情况,然后执行当前的最有操作。
小结
gevent的优势不仅仅是在代码中调用方便,厉害的是它拥有的monkey机制。假设你不愿意修改原来已经写好的python代码,但是又想充分利用gevent机制,那么你就可以用monkey来做到这一点。你所要做的就是在文件开头打一个patch,那么它就会自动替换你原来的thread、socket、time、multiprocessing等代码,全部变成gevent框架。这一切都是由gevent自动完成的。注意这个patch是在所有module都import了之后再打,否则没有效果。
甚至在编写的Web App代码的时候,不需要引入gevent的包,也不需要改任何代码,仅仅在部署的时候,用一个支持gevent的WSGI服务器,就可以获得数倍的性能提升。
本文简单介绍了gevent的使用,下一篇将对gevent的部分源码进行分析。