更新:導入一個有monkey.patch_all()的庫,應該用函數把它包起來,否則會拋出錯誤python
- RuntimeError: cannot release un-acquired lock 編程
在《python高性能編程》一書中做者開始講協程用的就是gevent模塊做爲例子。先舉一個簡單的例子:函數
協程極大大提升了性能
若是有100個foo,bar函數,每一個須要阻塞一秒,那麼串行執行須要100秒以上,而I/O多路複用執行,在一秒以後100個程序執行完畢。性能
協程提升性能的緣由ui
- 全部的阻塞請求同時發生,只要函數輸入到位
- cpu和操做系統的高度配合spa
-能夠這麼想象 操作系統
1 線程讀代碼,讀到一步須要去取數據,
2 線程本身不取數據,叫操做系統去取,取數據步驟參考途中1,3,4,2
3 操做系統取數據,線程就不讀代碼了,不幹其餘事,就在這等着
4 等數據到了,線程繼續讀代碼,按代碼幹事
線程
-重點來了,IO多路複用以後事情發生了變化:協程
- 線程很快找到查看了代碼,發現代碼中指出了有一些代碼是能夠同時執行的
- 好比下圖有4處能夠同時執行的代碼,
對象
-每一處代碼裏面可能也有阻塞,因爲更裏面的阻塞還不能發起請求主要先不關注,
- 線程拿起1處的代碼,向系統發起請求,不等待,回到代碼2處
- 再去操做系統處發送請求,如此反覆,直到代碼代表的特殊代碼塊都發起了請求
- 線程毫無停歇,只要操做系統使其獲得數據,他就運行代碼,並重復以上步驟
- 對比:在串行的狀況下,線程在1.1處停下等待0.1s,而IO多路複用已經把活全作完了
協程做用的範圍:
- 如下兩個條件同時具有
I/O密集型任務。
任務自己結果不受其餘任務
如何標記特殊代碼塊。(猴子補丁+gevent.spawn())
- 打上猴子補丁
from gevent import monkey
monkey.patch_all()
- gevent.spawn(function,*arg,**kwargs)
若是一個function內部沒有阻塞,那麼到這一步,正常執行
若是function能有阻塞,那麼它被凍結並被標記成特殊代碼塊
- 使用內置方法使其開始執行
gevent.joinall()生成對象是list類型
iwait生成對象是生成器類型
對單個對象使用.join()方法,可是沒法經過.value獲取函數返回值
對單個對象使用.get()方法,能夠直接運行並得到返回值,get(timeout=3)還能設置超時時間
- 無論哪一個方法,生成對象的元素使用.value,獲得那些特殊代碼塊的返回值,
若是沒有返回值,那就不用管
信號量(from gevent.lock import BoundedSemaphore)
- 生成計算器對象,並設置值
bigest=BoundedSemaphore(5)
生成最大數爲5的計數器
- 在代碼中調用
使用with方法
或使用bigest.acquire(), bigest.release()
只能用一次的超時控制(gevent.Timeout):
- 在.join()方法前加上
timeout=Timeout(2)
timeout.start()
- timeout支持with調用,其__enter__()方法調用了start(),不知道在函數內部調用會有什麼效果?
還有許多內容,但用好上面這些已能讓你代碼執行速度快上十倍了。
好吧,再來一點乾貨:不一樣線程內相互控制以及傳遞值
import gevent from gevent.event import AsyncResult
a = AsyncResult()
a.set('值')
a.get()='值'
解釋:a實例化時給全部a.get() 後面的代碼加鎖,而後a.set()解鎖,a.get()獲取一個值