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 協程池的使用
協程池相對要好用些,可以自定義一個協程池指定個數,把待執行的隊列(列表)丟進去,即可執行
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) result = mypool.map(download_img, gevent_data_list) print(result) except Exception as e: print("異常 {}".format(e)) if __name__ == "__main__": main()