python------線程
一、我們知道無論是創建多進程還是創建多線程池來解決問題,都要消耗一定的時間來創建進程、創建線程、以及管理他們之間的切換。
基於單線程來實現並發,這樣就可以節省創建線程進程所消耗的時間。
二、如何實現在兩個函數之間的切換?
def func1(): print(1) yield print(3) yield def func2(): g = func1() next(g) print(2) next(g) print(4) func2() ‘‘‘ 1 2 3 4 ‘‘‘切換1
def consumer():切換2while True: n = yield print(‘消費了一個包子%s‘%n) def producer(): g = consumer() next(g) for i in range(5): print(‘生產了包子%s‘%i) g.send(i) producer() ‘‘‘ 生產了包子0 消費了一個包子0 生產了包子1 消費了一個包子1 生產了包子2 消費了一個包子2 生產了包子3 消費了一個包子3 生產了包子4 消費了一個包子4 ‘‘‘
import time def consumer():yield無法做到遇到io阻塞‘‘‘任務1:接收數據,處理數據‘‘‘ while True: x=yield def producer(): ‘‘‘任務2:生產數據‘‘‘ g=consumer() next(g) for i in range(10000000): g.send(i) time.sleep(2) start=time.time() producer() #並發執行,但是任務producer遇到io就會阻塞住,並不會切到該線程內的其他任務去執行 stop=time.time() print(stop-start)
對於單線程下,程序中不可避免的會出現io操作,但如果我們能在自己的程序中(即用戶程序級別,而非操作系統級別)控制單線程下的多個任務能再一個任務遇到io阻塞時就切換到另外一個任務去計算,這樣就保證了該線程能夠最大限度地處於就緒態,即隨時都可以被cpu執行的狀態,相當於我們在用戶程序級別將自己的io操作最大限度地隱藏起來,從而可以迷惑操作系統,讓其看到:該線程好像是一直在計算,io比較少,從而更多的將cpu的執行權限分配給我們的線程。
三、協程
協程:是單線程下的並發,協程是一種用戶態的輕量級線程,即協程是由用戶程序自己控制調度的。
協程的本質:在但線程下,由用戶自己控制一個任務遇到io阻塞了就切換另外一個任務去執行,以此來提升效率。
需要強調的是:
1:python的線程屬於內核級別的,即由操作系統控制調度(如單線程遇到io或執行時間過長就會被迫交出cpu執行權限,切換其它線程運行)。
2:單線程內開啟協程,一旦遇到io,就會從應用程序級別(而非操作系統)控制切換,以此來提升效率(非io操作的切換與效率無關)
對比操作系統控制線程的切換,用戶在單線程內控制協程的切換的優缺點:
優點:
1.協程的切換開銷更小,屬於程序級別的切換,操作系統完全感知不到,因而更加輕量級。
2.單線程內就可以實現並發的效果,最大限度地利用cpu。
缺點:
1.協程的本質是單線程下,無法利用多核,可以是一個程序開啟多個進程,每個進程內開啟多個線程,每個線程內開啟協程。
2.協程指的是單個線程,因而一旦協程出現阻塞,將會阻塞整個線程。
協程的特點:
1.必須在只有一個單線程裏實現並發
2.修改共享數據不需加鎖
3.用戶程序裏自己保存多個控制流的上下文棧
4.附加:一個協程遇到IO操作自動切換到其它協程(如何實現檢測IO,yield、greenlet都無法實現,就用到了gevent模塊(select機制))
四、greenlet模塊
from greenlet import greenlet def eat(name): print(‘%s eat 1‘ %name) g2.switch(‘haha‘) print(‘%s eat 2‘ %name) g2.switch() def play(name): print(‘%s play 1‘ %name) g1.switch() print(‘%s play 2‘ %name) g1=greenlet(eat) g2=greenlet(play) g1.switch(‘hjm‘)#可以在第一次switch時傳入參數,以後都不需要 ‘‘‘ hjm eat 1 haha play 1 hjm eat 2 haha play 2 ‘‘‘greenlet實現狀態切換
import time from greenlet import greenlet # 在單線程中切換狀態的模塊 def eat1(): print(‘吃雞腿1‘) g2.switch() time.sleep(5) print(‘吃雞翅2‘) g2.switch() def eat2(): print(‘吃餃子1‘) g1.switch() time.sleep(3) print(‘白切雞‘) g1 = greenlet(eat1) g2 = greenlet(eat2) g1.switch() ‘‘‘ 吃雞腿1 吃餃子1 吃雞翅2 白切雞 ‘‘‘greenlet實現狀態切換2
單純的切換(在沒有io的情況下或者沒有重復開辟內存空間的操作),反而會降低程序的執行速度。
#順序執行 import time def f1(): res=1 for i in range(100000000): res+=i def f2(): res=1 for i in range(100000000): res*=i start=time.time() f1() f2() stop=time.time() print(‘run time is %s‘ %(stop-start)) # run time is 10.494175910949707 #切換 from greenlet import greenlet import time def f1(): res=1 for i in range(100000000): res+=i g2.switch() def f2(): res=1 for i in range(100000000): res*=i g1.switch() start=time.time() g1=greenlet(f1) g2=greenlet(f2) g1.switch() stop=time.time() print(‘run time is %s‘ %(stop-start)) # run time is 63.0725622177124效率對比
greenlet只是提供了一種比generator更加便捷的切換方式,當切到一個任務執行時如果遇到io,那就原地阻塞,仍然是沒有解決遇到IO自動切換來提升效率的問題。
五、gevent模塊
python------線程