python 之gevent 协程操作
gevent的简介
gevent是一个基于协程的python网络库,在遇到IO阻塞时,程序会自动进行切换,可以让我们用同步的方式写异步IO代码。
因为python线程的性能问题,在python中使用多线程运行代码经常不能达到预期的效果。而有些时候我们的逻辑中又需要开更高的并发,或者简单的说,就是让我们的代码跑的更快,在同样时间内执行更多的有效逻辑、减少无用的等待。gevent就是一个现在很火、支持也很全面的python第三方协程库。
gevent是python的一个并发框架,以微线程greenlet为核心,使用了epoll事件监听机制以及诸多其他优化而变得高效。而且其中有个monkey类,将现有基于Python线程直接转化为greenlet(类似于打patch)。在运行时的具体流程大概就是:
当一个greenlet遇到IO操作时,比如访问网络/睡眠等待,就自动切换到其他的greenlet,等到IO操作完成,再在适当的时候切换回来继续执行。由于IO操作非常耗时,经常使程序处于等待状态,有了gevent为我们自动切换协程,就保证总有greenlet在运行,而不是等待IO。同时也因为只有一个线程在执行,会极大的减少上下文切换的成本。
gevent是python的一个并发框架,以微线程greenlet为核心,使用了epoll事件监听机制以及诸多其他优化而变得高效。而且其中有个monkey类,将现有基于Python线程直接转化为greenlet(类似于打patch)。在运行时的具体流程大概就是:
当一个greenlet遇到IO操作时,比如访问网络/睡眠等待,就自动切换到其他的greenlet,等到IO操作完成,再在适当的时候切换回来继续执行。由于IO操作非常耗时,经常使程序处于等待状态,有了gevent为我们自动切换协程,就保证总有greenlet在运行,而不是等待IO。同时也因为只有一个线程在执行,会极大的减少上下文切换的成本。
gevent基本使用 不加gevent.monkey是顺序执行起不到协程的作用
# -*- 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])
运行后输出如下图所示是顺序执行,并没有切换:
gevent的正确使用
import gevent import gevent.pool import gevent.monkey gevent.monkey.patch_all() # 分布式冲突 import requests def download_img(t_event_data): """ 保存图片到本地 :return: """ try: index = t_event_data["index"] num = t_event_data["num"] r = requests.get("https://wwww.baidu.com") print("index:{} num:{}".format(index, num)) except Exception as e: print("下载异常:{}".format(e)) def main(): try: num_list = [i for i in range(1, 100)] # 相当于起了100个协程池 gevent_data_list = [] for index, num in enumerate(num_list): t_event_data = {} t_event_data["index"] = index t_event_data["num"] = num gevent_data_list.append(gevent.spawn(download_img, t_event_data)) gevent.joinall(gevent_data_list) except Exception as e: print("异常 {}".format(e)) if __name__ == "__main__": main()
gevent 协程池的使用
协程池相对要好用些,可以自定义一个协程池指定个数,把待执行的队列(列表)丢进去,即可执行
gevent.joinall()等待所有协程结束 timeout设置每个协程超时时间
import gevent import gevent.pool import gevent.monkey gevent.monkey.patch_all() # 分布式冲突 import requests def download_img(t_event_data): """ 保存图片到本地 :return: """ try: index = t_event_data["index"] num = t_event_data["num"] r = requests.get("https://wwww.baidu.com") print("index:{} num:{}".format(index, num)) except Exception as e: print("下载异常:{}".format(e)) def main(): try: num_list = [i for i in range(1, 100)] #待处理的数据 gevent_data_list = [] mypool = gevent.pool.Pool(10) # 定义协程池个数 for index, num in enumerate(num_list): t_event_data = {} t_event_data["index"] = index t_event_data["num"] = num gevent_data_list.append(t_event_data) # 协程设置超时时间 #gevent_data_list.append(mypool.spawn(t_event_data, t_event_data)) result = mypool.map(download_img, gevent_data_list) #result = gevent.joinall(gevent_data_list, timeout=5) # timeout每个协程设置超时时间 print(result) except Exception as e: print("异常 {}".format(e)) if __name__ == "__main__": main()