協程,異步IO
阿新 • • 發佈:2018-01-03
com https 簡化 opened hide str pen pre star
協程
協程,又稱微線程,纖程。英文名Coroutine。一句話說明什麽是線程:協程是一種用戶態的輕量級線程。實現單線程的並發。
協程擁有自己的寄存器上下文和棧。協程調度切換時,將寄存器上下文和棧保存到其他地方,在切回來的時候,恢復先前保存的寄存器上下文和棧。因此:
協程能保留上一次調用時的狀態(即所有局部狀態的一個特定組合),每次過程重入時,就相當於進入上一次調用的狀態,換種說法:進入上一次離開時所處邏輯流的位置。
協程的好處:
- 無需線程上下文切換的開銷
- 無需原子操作鎖定及同步的開銷(協程本身就是單線程,單線程操作是原子的不需要加鎖)
- "原子操作(atomic operation)是不需要synchronized",所謂原子操作是指不會被線程調度機制打斷的操作;這種操作一旦開始,就一直運行到結束,中間不會有任何 context switch (切換到另一個線程)。原子操作可以是一個步驟,也可以是多個操作步驟,但是其順序是不可以被打亂,或者切割掉只執行部分。視作整體是原子性的核心。
- 方便切換控制流,簡化編程模型
- 高並發+高擴展性+低成本:一個CPU支持上萬的協程都不是問題。所以很適合用於高並發處理。
缺點:
- 無法利用多核資源:協程的本質是個單線程,它不能同時將 單個CPU 的多個核用上,協程需要和進程配合才能運行在多CPU上.當然我們日常所編寫的絕大部分應用都沒有這個必要,除非是cpu密集型應用。
- 進行阻塞(Blocking)操作(如IO時)會阻塞掉整個程序
協程如何實現在單線程裏面多並發的效果? 其核心就是遇到 IO操作就切換,那麽怎麽知道 IO 操作已完成,自動切換回去呢?
協程的實現方式:
yield實現協程
greenlet是一個用C實現的協程模塊,相比與python自帶的yield,它可以使你在任意函數之間隨意切換,而不需把這個函數先聲明為generator
Gevent 是一個第三方庫,可以輕松通過gevent實現並發同步或異步編程,在gevent中用到的主要模式是Greenlet, 它是以C擴展模塊形式接入Python的輕量級協程。 Greenlet全部運行在主程序操作系統進程的內部,但它們被協作式地調度
windows 下安裝gevent:
https://pypi.python.org/pypi/greenlet/0.4.12 下載greenlet,gevent
pip3 install greenlet-0.4.12-cp35-cp35m-win_amd64.whl
gevent的使用:
join方法:等待協程執行完在退出,和多線程類似
from gevent import monkey import time monkey.patch_all() import gevent from urllib.request import urlopen def f(url): print(‘GET: %s‘ % url) resp = urlopen(url) data = resp.read() print(‘%d bytes received from %s.‘ % (len(data), url)) start_time_1 = time.time() i = ‘http://www.cnblogs.com/alex3714/articles/5248247.html‘ a= gevent.spawn(f,i) a.join() #等待gevent執行完畢,否則程序直接退出等不到結果。不加join() 程序直接退出和多線程類似 print("cost is %s..." % str(time.time() - start_time_1))
#執行結果:
GET: http://www.cnblogs.com/alex3714/articles/5248247.html
92210 bytes received from http://www.cnblogs.com/alex3714/articles/5248247.html.
cost is 9.593548774719238...
#不加join的結果:
cost is 0.003000020980834961...
from gevent import monkey import time monkey.patch_all() import gevent from urllib.request import urlopen def f(url): print(‘GET: %s‘ % url) resp = urlopen(url) data = resp.read() print(‘%d bytes received from %s.‘ % (len(data), url)) start_time = time.time() gevent.joinall([ #gevent.joinall 等待所有的協程都執行完畢後程序在退出 gevent.spawn(f, ‘http://www.cnblogs.com/alex3714/articles/5248247.html‘), gevent.spawn(f, ‘https://github.com/‘), ]) print("cost is %s..." % str(time.time() - start_time))
#執行結果:
GET: http://www.cnblogs.com/alex3714/articles/5248247.html
GET: https://github.com/
51594 bytes received from https://github.com/.
92210 bytes received from http://www.cnblogs.com/alex3714/articles/5248247.html.
cost is 9.431539297103882...
不加join方法,如何讓協程不退出?
from gevent import monkey import time monkey.patch_all() import gevent from urllib.request import urlopen def g(url): print(‘GET: %s‘ % url) resp = urlopen(url) data = resp.read() print(‘%d bytes received from %s.‘ % (len(data), url)) i = ‘http://www.cnblogs.com/alex3714/articles/5248247.html‘ while True: time.sleep(10) a = gevent.spawn(g, i) print("ok?") #執行結果: #首先等待10s,直接跳過a,輸出ok。再次等待10s的過程中得到a的結果。因為沒有join,通過循環保證線程不退出從而得到協程的結果 ok? GET: http://www.cnblogs.com/alex3714/articles/5248247.html 92210 bytes received from http://www.cnblogs.com/alex3714/articles/5248247.html. ok? GET: http://www.cnblogs.com/alex3714/articles/5248247.htmlView Code
協程,異步IO