gevent 对协和的支持,本质上是greenlet在实现切换工作。工作流程如下:假如进行访问网络的IO操作时,出现阻塞,greenlet就显式切换到另一段没有被阻塞的代码段执行,直到原先的阻塞状态消失以后,再自动切换回原来的代码段继续处理。因此,greenlet是一种合理安排的串行方式。
由于IO操作非常耗时,经常使程序处于等待状态,有了gevent为我们自动切换协程,就保证总有greenlet在运行,而不是等待IO,这就是协程一般比多线程效率高的原因。由于切换是在IO操作时自动完成,所有gevent需要修改Python自带的一些标准库,将一些常见的阻塞,如socket select等地方实现协程跳转,这一过程在启动时通过monkey patch完成。
1 from gevent import monkey 2 monkey.patch_all() 3 import gevent 4 import requests 5 6 def run_task(url): 7 print('Visit --> %s' % url) 8 try: 9 res = requests.get(url) 10 data = res.text 11 print('%d bytes received from %s.' % (len(data), url)) 12 except Exception as e: 13 print(e) 14 15 if __name__ == '__main__': 16 urls = ['https://github.com', 'https://www.python.org', 'http://www.cnblogs.com'] 17 greenlets = [gevent.spawn(run_task, url) for url in urls] # 生成协程 18 gevent.joinall(greenlets) # 添加协程任务并且启动 所以有协程都是在同一个线程中完成的
还提供了Pool的支持
1 from gevent import monkey; monkey.patch_all() 2 import requests 3 from gevent.pool import Pool 4 5 def run_task(url): 6 print('Visit --> %s' % url) 7 try: 8 res = requests.get(url) 9 data = res.text 10 print('%d bytes received from %s.' % (len(data), url)) 11 except Exception as e: 12 print(e) 13 return 'url: %s ---> finish' % url 14 15 if __name__ == '__main__': 16 pool = Pool(2) # 创建池 17 urls = ['https://github.com', 'https://www.python.org', 'http://www.cnblogs.com'] 18 results = pool.map(run_task, urls) 19 print(results)
以上参考自《Python爬虫开发与项目实战》 机械工业出版社