實現並發-協程gevent
阿新 • • 發佈:2019-05-09
中大 nts 替換 tar reading str end def ssl
實現並發-協程gevent
文檔地址
協程
#協程:非搶占式切換,程序切換(用戶態切換) #沒有硬件層面的資源消耗,只是一個線程下的切換 #協程本質就是一個線程 # 1.沒有切換的消耗 # 2.沒有鎖的概念 #缺點:不能使用多核 #解決辦法:多進程+協程,在多進程裏使用協程。 # A -->B --> A # 執行A,遇到需要切換的地方就切換到B,執行完B就切換到A繼續執行 # 這裏我們要知道,什麽時候切換,怎麽切換,怎麽保證切換回來繼續在切走的位置繼續執行(保持狀態) #什麽時候切換? #比如,有兩個程序都在進行IO情況,在阻塞的時候就進行切換。#切換和保存狀態我們適用關鍵字yeild # yeild切換出去 # 而切換進來有兩種方式con1.__next__()這種方式,con1.send(n) # 第一種方式能切換進來執行代碼,第二種方式可以傳遞參數進行交互 # yeild相當於return多了保存狀態的功能,下次還可以切換回來,繼續接著上次切換出去的地方執行
yeild實現協程
import time def cumtom(name): print(‘%s準備吃包子‘ %name) time.sleep(1) while 1: count=yield print(‘%s吃到第%d個包子‘ %(name,count)) def producter(): con1.__next__() con2.__next__() n=1 while 1: time.sleep(1) print(‘已經生產出來%d、%d個包子‘ %(n,n+1)) #通過send方法通知 con1.send(n) con2.send(n+1) n+=2 #cumtom函數裏面有yield,這裏傳遞參數,會創建一個生成器對象,(提前做了預處理)con1=cumtom(‘cumtom1‘) con2=cumtom(‘cumtom2‘) producter()
gevent
# gevent # gevent是一個著名的開發框架。Gevent是python的第三方庫,提供了比較完善的對協程的支持 # Gevent是一個基於libev的並發庫。它為各種並發和網絡相關的任務提供了整潔的API。 # Gevent的基本思想是:當遇到IO操作時,會自動寫換到其他gevent,再在適當的時間切回來繼續執行。這樣就減少了IO操作時的等待耗時,從而能夠提高硬件資源的利用率。 # gevent特點 # 1.基於libev的高效時間循環 # Libev是什麽? # Libev是高性能事件循環/事件模型的網絡庫,並且包含大量新特性。它是繼lievent之後的一套全新網絡庫。它追求的目標:速度更快,bug更少,特性更多,體積更小。它和libevent很像,按照作者的介紹,可以作為libevent的替代者,能夠提供更高的性能,並且不需要復雜配置。 # 2.基於greenlet的輕量級執行單元 # 3.重用Python標準庫API內容(比如Events,Queues) # 4.socket協同ssl # 5.利用線程池或者c-cares 來執行DNS查詢 # 6.靈猴補丁能夠協同第三方庫
1.gevent中一個很大的改進就是將阻塞IO改為非阻塞IO; 2.比如gevent.socket.patch就是將項目中阻塞socket變為非阻塞;
greenlet/eventlet/gevent的關系
Greelent實現了一個比較易用(相比yeild)的協程切換的庫。但是greenlet沒有自己的調度過程,所以一般不會直接使用。
Eventlet在Greenlet的基礎上實現了自己的GreenThread,實際上就是greenlet類的擴展封裝,而與Greenlet的不同是,Eventlet實現了自己調度器稱為Hub,Hub類似於Tornado的IOLoop,是單實例的。
在Hub中有一個event loop,根據不同的事件來切換到對應的GreenThread。同時Eventlet還實現了一系列的補丁來使Python標準庫中的socket等等module來支持GreenThread的切換。
Eventlet的Hub可以被定制來實現自己調度過程。
Gevent基於libev和Greenlet。不同於Eventlet的用python實現的hub調度,Gevent通過Cython調用libev來實現一個高效的event loop調度循環。
同時類似於Eventlet,Gevent也有自己的monkey_patch,在打了補丁後,完全可以使用python線程的方式來無感知的使用協程,減少了開發成本。
gevent猴子補丁
猴子補丁monkey_patch,將標準庫中大部分的阻塞式調用替換成非阻塞的方式,包括socket、ssl、threading、select、httplib等。通過monkey.patch_xxx()來打補丁。
按照gevent文檔中的建議,應該將猴子補丁的代碼盡可能早的被調用,這樣可以避免一些奇怪的異常。
gevent實現了協程的創建、切換和調度,本身是同步的,而猴子補丁將gevent調用的阻塞庫變成非阻塞的,兩者配合實現了高性能的協程。
#優點:遇到IO操作自動切換 # import gevent # import requests,time # # def get(url): # print(‘GET:%s‘ %url) # response=requests.get(url) # data=response.text # print(data) # # # gevent.joinall([ # gevent.spawn(get,‘http://python.jobbole.com/84301/‘), # gevent.spawn(get,‘https://www.cnblogs.com/xybaby/p/6337944.html‘), # # gevent.spawn(get,‘‘), # # gevent.spawn(get,‘‘) # ]) #
greenlet(輕量級並發編程庫)
greenlet如何實現並發
1.greenlet是stackless python(支持微線程tasklet的CPython版本)的副產品。Tasklet以偽並發運行著(如果在單個或者很少的系統級線程內); 2.greenlet是一個原始的微線程的概念,沒有調度,可以稱為協程。所以green需要自己調度; 3.如果有阻塞調用,將greenlet主動切換出去。 greenlet與python線程關系 單個線程內可以運行任意哥greenlet微線程,不同線程之間不能切換greenlet。
#這裏的切換就更簡單了調用switch()就可以進行切換,而切換的發起端就是調用switch()的一端(先執行) #缺點:手動切換 # import greenlet # # # def test1(): # print(1) # g2.switch() # print(3) # g2.switch() # # # def test2(): # print(2) # g1.switch() # print(4) # # # g1=greenlet.greenlet(test1) # g2=greenlet.greenlet(test2) # # g2.switch()
實現並發-協程gevent