一、協程簡介 什麼是協程? 協程,又稱微線程,線程,英文名Coroutine。協程是一種用戶態的輕量級線程 協程擁有自己的寄存器上下文和棧。 簡單來說,協程就是來回切換,當遇到IO操作,如讀寫文件,網路操作時,就跳到另一個線程執行,再遇到IO操作,又跳回來。不斷的跳過去跳過來執行,因為速度很快,所以 ...
一、協程簡介
什麼是協程?
協程,又稱微線程,線程,英文名Coroutine。協程是一種用戶態的輕量級線程
協程擁有自己的寄存器上下文和棧。
簡單來說,協程就是來回切換,當遇到IO操作,如讀寫文件,網路操作時,就跳到另一個線程執行,再遇到IO操作,又跳回來。不斷的跳過去跳過來執行,因為速度很快,所以看起來就像是執行的併發,實質上是單線程。
協程的好處:
- 無需線程上下文切換的開銷
- 無需原子操作鎖定及同步的開銷
- 方便切換控制流,簡化編程模型
- 高併發+高擴展性+低成本
- 一個CPU支持上萬的協程都不是問題。所以很適合用於高併發處理
協程的缺點:
1.無法利用多核資源
協程的本質是個單線程,不能同時將單個CPU 的多個核用上。
協程需要和進程配合才能運行在多CPU上,日常所編寫的絕大部分應用都沒有這個必要,除非是cpu密集型應用
2.進行阻塞(Blocking)操作(如IO時,讀寫文件,網路)會阻塞掉整個程式
二、yield實現最簡單協程效果
在單線程中實現併發效果
以簡單的生產者消費者模型為例,生產者廚師做包子,消費者吃包子
代碼1:
說明:1個生產者,每次做1個包子,只做5個,2個消費者
1 import time 2 import queue 3 4 def consumer(name): 5 print("%s準備開始吃包子啦..." % name) 6 while True: 7 new_baozi = yield 8 print("[%s] 正在吃包子 %s" % (name,new_baozi)) 9 #time.sleep(1) 10 11 def producer(): 12 r = con.__next__() 13 r = con2.__next__() 14 n = 0 15 while n < 5: # 生產者只做5個包子 16 n += 1 17 print("\033[32;1m[producer]\033[0m做好了新包子 %s" % n ) 18 time.sleep(1) 19 con.send(n) 20 con2.send(n) 21 22 if __name__ == '__main__': 23 # 定義2個消費者、1個生產者 24 con = consumer("c1") 25 con2 = consumer("c2") 26 p = producer() 27 28 # 顯示結果: 29 # c1準備開始吃包子啦... 30 # c2準備開始吃包子啦... 31 # [producer]做好了新包子 1 32 # [c1] 正在吃包子 1 33 # [c2] 正在吃包子 1 34 # [producer]做好了新包子 2 35 # [c1] 正在吃包子 2 36 # [c2] 正在吃包子 2 37 # [producer]做好了新包子 3 38 # [c1] 正在吃包子 3 39 # [c2] 正在吃包子 3 40 # [producer]做好了新包子 4 41 # [c1] 正在吃包子 4 42 # [c2] 正在吃包子 4 43 # [producer]做好了新包子 5 44 # [c1] 正在吃包子 5 45 # [c2] 正在吃包子 5
代碼2:
說明:生產者只做5次包子,每次2個,2個消費者
1 import time 2 import queue 3 4 def consumer(name): 5 print("%s準備開始吃包子啦..." % name) 6 while True: 7 new_baozi = yield 8 print("[%s] 正在吃包子 %s" % (name,new_baozi)) 9 #time.sleep(1) 10 11 def producer(name): 12 r = con.__next__() 13 r = con2.__next__() 14 n = 0 15 m = 0 16 count = 0 17 while count < 5: # 生產者只做5次包子,每次2個 18 n = m + 1 19 m = n + 1 20 count += 1 21 print("\033[32;1m廚師%s\033[0m做好了新包子 %s" % (name,n) ) 22 print("\033[32;1m廚師%s\033[0m做好了新包子 %s" % (name,m) ) 23 time.sleep(1) 24 con.send(n) 25 con2.send(m) 26 27 if __name__ == '__main__': 28 # 定義2個消費者、1個生產者 29 con = consumer("莉莉") 30 con2 = consumer("靜靜") 31 p = producer("小白") 32 33 # 顯示結果: 34 # 莉莉準備開始吃包子啦... 35 # 靜靜準備開始吃包子啦... 36 # 廚師小白做好了新包子 1 37 # 廚師小白做好了新包子 2 38 # [莉莉] 正在吃包子 1 39 # [靜靜] 正在吃包子 2 40 # 廚師小白做好了新包子 3 41 # 廚師小白做好了新包子 4 42 # [莉莉] 正在吃包子 3 43 # [靜靜] 正在吃包子 4 44 # 廚師小白做好了新包子 5 45 # 廚師小白做好了新包子 6 46 # [莉莉] 正在吃包子 5 47 # [靜靜] 正在吃包子 6 48 # 廚師小白做好了新包子 7 49 # 廚師小白做好了新包子 8 50 # [莉莉] 正在吃包子 7 51 # [靜靜] 正在吃包子 8 52 # 廚師小白做好了新包子 9 53 # 廚師小白做好了新包子 10 54 # [莉莉] 正在吃包子 9 55 # [靜靜] 正在吃包子 10
用sleep,模擬IO操作阻塞
一遇到阻塞,怎麼把阻塞丟給操作系統,yield實現不了,用協程可以實現
三、greenlet模塊
greenlet,協程模塊,是一個第三方模塊,不是Python自帶的,需要安裝才能夠使用。
導入模塊命令:from greenlet import greenlet
代碼示例:
1 from greenlet import greenlet 2 3 def test1(): 4 print(12) 5 gr2.switch() 6 print(34) 7 gr2.switch() 8 9 def test2(): 10 print(56) 11 gr1.switch() 12 print(78) 13 14 gr1 = greenlet(test1) 15 gr2 = greenlet(test2) 16 gr1.switch() 17 18 # 顯示結果: 19 # 12 20 # 56 21 # 34 22 # 78 23 24 # 說明:先執行test1函數,列印了12,切換到test2函數,列印出56,再返回到test1(先前切換的地方),列印34,又切換到test,列印78 25 # 協程,就是來回的切換greenlet模塊協程示例
先寫到這裡...