協程

協程

協程,又稱微線程,纖程。英文名Coroutine。一句話說明什麼是線程:協程是一種用戶態的輕量級線程python

協程擁有本身的寄存器上下文和棧。協程調度切換時,將寄存器上下文和棧保存到其餘地方,在切回來的時候,恢復先前保存的寄存器上下文和棧。所以:編程

協程能保留上一次調用時的狀態(即全部局部狀態的一個特定組合),每次過程重入時,就至關於進入上一次調用的狀態,換種說法:進入上一次離開時所處邏輯流的位置。併發

咱們給協程一個標準定義,即符合什麼條件就能稱之爲協程:異步

  1. 必須在只有一個單線程裏實現併發
  2. 修改共享數據不需加鎖
  3. 用戶程序裏本身保存多個控制流的上下文棧
  4. 一個協程遇到IO操做自動切換到其它協程

 

協程的好處:socket

  • 無需線程上下文切換的開銷
  • 無需原子操做鎖定及同步的開銷
    •   "原子操做(atomic operation)是不須要synchronized",所謂原子操做是指不會被線程調度機制打斷的操做;這種操做一旦開始,就一直運行到結束,中間不會有任何 context switch (切換到另外一個線程)。原子操做能夠是一個步驟,也能夠是多個操做步驟,可是其順序是不能夠被打亂,或者切割掉只執行部分。視做總體是原子性的核心。
  • 方便切換控制流,簡化編程模型
  • 高併發+高擴展性+低成本:一個CPU支持上萬的協程都不是問題。因此很適合用於高併發處理。

 

缺點:異步編程

  • 沒法利用多核資源:協程的本質是個單線程,它不能同時將 單個CPU 的多個核用上,協程須要和進程配合才能運行在多CPU上.固然咱們平常所編寫的絕大部分應用都沒有這個必要,除非是cpu密集型應用。
  • 進行阻塞(Blocking)操做(如IO時)會阻塞掉整個程序

舉一個栗子:高併發

def consumer(name):
    print("\033[33;1m[%s]\033[0m 開始吃蛋糕....." %name)
    while True:
        dangao = yield
        print("\033[35;1m[%s]\033[0m 吃了蛋糕 %s" %(name,dangao))

def producer(name):
    c1.__next__()#啓動生成器
    c2.__next__()
    n = 0
    while n < 5:
        n+=1
        print("\033[32;1m[%s]\033[0m 開始製做蛋糕 %s......" %(name,n))
        c1.send(n)
        c2.send(n)


if __name__ == "__main__":
    c1 = consumer("mogu")
    c2 = consumer("wanzi")
    p1 = producer("HC")

上面的例子使用yield,完成了一個貌似是協程的生產者消費者模型,可是若是在生產者或者消費者中添加例如sleep之類的操做,這個程序就會變得很慢,與咱們但願出現阻塞操做就切換還差了一些atom

 

python中有一個模塊叫作greenlet,這個模塊能夠作到出現阻塞就切換到別的操做,也能夠切換回來spa

看下面的栗子:操作系統

from greenlet import greenlet
import time

def wanzi():
    print("wanzi 正在吃蛋糕,看看mogu在幹什麼")
    gr2.switch()
    time.sleep(2)
    print("wanzi 還在吃蛋糕,看看mogu在幹什麼")
    gr2.switch()

def mogu():
    print("mogu 正在舔爪子,看看wanzi在幹什麼")
    gr1.switch()
    print("mogu 正在打滾。")
    gr1.switch()

gr1 = greenlet(wanzi)
gr2 = greenlet(mogu)
gr1.switch()

使用greenlet實現了切換的操做,可是阻塞仍是沒有解決

Gevent 是一個第三方庫,能夠輕鬆經過gevent實現併發同步或異步編程,在gevent中用到的主要模式是Greenlet, 它是以C擴展模塊形式接入Python的輕量級協程。 Greenlet所有運行在主程序操做系統進程的內部,但它們被協做式地調度。

栗子:

import gevent
import time

def wanzi():
    print("wanzi 正在玩玩具.....看看mogu在幹什麼....")
    gevent.sleep(10)
    print("wanzi 看着mogu滿屋跑,哈哈大笑")

def mogu():
    print("mogu正在舔爪子,看看HC在幹什麼....")
    gevent.sleep(2)
    print("mogu添完爪子,正在滿屋亂跑...")

def HC():
    print("HC 正在給wanzi準備吃的...看看wanzi在幹什麼")
    gevent.sleep(1)
    print("HC 正在給wanzi準備吃的...看看wanzi在幹什麼")
    gevent.sleep(1)
    print("HC 正在給wanzi準備吃的...看看wanzi在幹什麼")
    gevent.sleep(1)
    print("HC 正在給wanzi準備吃的...看看wanzi在幹什麼")
    gevent.sleep(1)
    print("HC 正在給wanzi準備吃的...看看wanzi在幹什麼")
    gevent.sleep(1)
    print("HC 正在給wanzi準備吃的...看看wanzi在幹什麼")
    gevent.sleep(1)
    print("HC 準備喂wanzi吃飯")

start_time = time.time()
gevent.joinall([
    gevent.spawn(wanzi),
    gevent.spawn(mogu),
    gevent.spawn(HC)
])
stop_time = time.time()
print("cost : %s" %(stop_time-start_time))

使用gevent實現了遇到阻塞(I/O)就自動切換的功能

 

下面是一組使用協程實現socket大併發的例子

server

相關文章
相關標籤/搜索