在操做系統中進程是資源分配的最小單位,線程是CPU調度的最小單位。python
併發的本質:切換+保存狀態。程序員
cpu正在運行一個任務,會在兩種狀況下切走去執行其餘的任務(切換由操做系統強制控制),一種狀況是該任務發生了阻塞,另一種狀況是該任務計算的時間過長。併發
在介紹進程理論時,說起進程的三種執行狀態,而線程纔是執行單位,因此也能夠將上圖理解爲線程的三種狀態。函數
其中併發並不能提高效率,只是爲了讓cpu可以雨露均沾,實現看起來全部任務都被「同時」執行的效果,若是多個任務都是純計算的,這種切換反而會下降效率。spa
協程:是單線程下的併發,又稱微線程,纖程。英文名Coroutine。操作系統
一句話說明什麼是協程:協程是一種用戶態的輕量級線程,即協程是由用戶程序本身控制調度的,單線程下實現併發。線程
須要強調的是:code
對比操做系統控制線程的切換,用戶在單線程內控制協程的切換。協程
重點:遇到io切換的時候纔有意義進程
具體: 協程概念本質是程序員抽象出來的,操做系統根本不知道協程存在,也就說來了一個線程我本身遇到io 我本身線程內部直接切到本身的別的任務上了,操做系統跟本發現不了,也就是實現了單線程下效率最高.
優勢:
缺點:
特色:
import time def eat(): print('eat 1') # 瘋狂的計算呢沒有io time.sleep(2) # for i in range(100000000): # i+1 def play(): print('play 1') # 瘋狂的計算呢沒有io time.sleep(3) # for i in range(100000000): # i+1 play() eat() # 5s
在單線程裏,利用yield來實現協程,這是一個沒有意義的攜程(由於咱們說過協程要作在有io的狀況下才有意義)
import time def func1(): while True: 1000000+1 yield def func2(): g = func1() for i in range(100000000): i+1 next(g) start = time.time() func2() stop = time.time() print(stop - start) # 17.68560242652893
對比上面yeild切換運行的時間,反而比咱們單獨取執行函數串行更消耗時間,因此上面實現的攜程是沒有意義的。
import time def func1(): for i in range(100000000): i+1 def func2(): for i in range(100000000): i+1 start = time.time() func1() func2() stop = time.time() print(stop - start) # 12.08229374885559
協程的本質就是在單線程下,由用戶本身控制一個任務遇到io阻塞了就切換另一個任務去執行,以此來提高效率。爲了實現它,咱們須要找尋一種能夠同時知足如下條件的解決方案:
重點:使用gevent來實現協程是能夠的,可是咱們說過協程最主要是遇到IO纔有意義,可是剛好這個gevent模塊作不到協程的真正的意義,也就是說這個而模塊他檢測不到IO
但用gevent模塊是檢測不到IO的,也就是說這樣寫一樣是沒有意義的
下面程序裏的gevent是一個類
gevent.spawn本質調用了gevent.greenlet.Greenlet的類的靜態方法spawn:
@classmethod def spawn(cls, *args, **kwargs): g = cls(*args, **kwargs) g.start() return g
這個類方法調用了Greenlet類的兩個函數,*__init_*_ 和 start. init函數中最爲關鍵的是這段代碼:
def __init__(self, run=None, *args, **kwargs): greenlet.__init__(self, None, get_hub()) # 將新創生的greenlet實例的parent一概設置成hub if run is not None: self._run = run
# 在這段程序咱們發現,這段程序並無實現碰見IO的時候,用戶模cpu實現任務切換 import gevent import time def eat(): print('eat 1') time.sleep(2) print('eat 2') def play(): print('play 1') # 瘋狂的計算呢沒有io time.sleep(3) print('play 2') start = time.time() g1 = gevent.spawn(eat) g2 = gevent.spawn(play) g1.join() g2.join() end = time.time() print(end-start) 5.0041022300720215 ''' 結果: eat 1 eat 2 play 1 play 2 5.004306077957153 '''
重點二:使用gevent的一個補丁來實現,經過gevent類來實現真正有意義的協程,用戶真正的實現裏以操做系統發現不了的方式,模擬了碰見IO的時候實現任務之間的來回切換
注意:這裏再次強調,協程的本質意義是在單線程內實現任務的保存狀態加切換,而且真正的協程必須是在遇到IO的狀況
from gevent import monkey;monkey.patch_all() import gevent import time def eat(): print('eat 1') time.sleep(2) print('eat 2') def play(): print('play 1') # 瘋狂的計算呢沒有io time.sleep(3) print('play 2') start = time.time() g1 = gevent.spawn(eat) g2 = gevent.spawn(play) g1.join() g2.join() end = time.time() print(end-start)# 3.003168821334839 ''' 結果: eat 1 play 1 eat 2 play 2 3.003168821334839 '''