1. 程式人生 > >協程,異步IO

協程,異步IO

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.html
View Code

協程,異步IO