協程

引子:

以前咱們學習了線程,進程的概念,瞭解了在操做系統中進程使資源分配的最小單位,線程使CPU調度的最小單位,按道理來講咱們已經算是把CPU的利用率提升了不少了,可是咱們不管使建立進程仍是建立多線程來解決問題,都要消耗必定的時間來建立進程,建立線程,以及管理他們之間的切換python

咱們知道線程是基於進程來實現的,那麼基於線程來實現併發又成爲一個新的課題,即只用一個主線程(很明顯線程可利用的CPU只有一個),狀況下實現併發,這樣就能夠節省建立線程所消耗的時間了編程

 

協程介紹

協程:是單線程下的併發,又稱微線程,纖程。英文名Coroutine。一句話說明什麼是線程:協程是一種用戶態的輕量級線程,即協程是由用戶程序本身控制調度的。、多線程

 

協程多與線程進行比較

一、一個線程能夠多個協程,一個進程也能夠單獨擁有多個協程,這樣python中則能使用多核CPU。併發

二、線程進程都是同步機制,而協程則是異步異步

三、協程能保留上一次調用時的狀態,每次過程重入時,就至關於進入上一次調用的狀態ide

須要強調的是:異步編程

#1. python的線程屬於內核級別的,即由操做系統控制調度(如單線程遇到io或執行時間過長就會被迫交出cpu執行權限,切換其餘線程運行)
#2. 單線程內開啓協程,一旦遇到io,就會從應用程序級別(而非操做系統)控制切換,以此來提高效率(!!!非io操做的切換與效率無關)

對比操做系統控制線程的切換,用戶在單線程內控制協程的切換函數

優勢以下:學習

#1. 協程的切換開銷更小,屬於程序級別的切換,操做系統徹底感知不到,於是更加輕量級
#2. 單線程內就能夠實現併發的效果,最大限度地利用cpu

缺點以下:spa

#1. 協程的本質是單線程下,沒法利用多核,能夠是一個程序開啓多個進程,每一個進程內開啓多個線程,每一個線程內開啓協程
#2. 協程指的是單個線程,於是一旦協程出現阻塞,將會阻塞整個線程

協程是輕型線程,線程是輕型進程,協程是線程開啓就自動存在的

協程並非實際存在的實體

它的本質就是一個線程的多個部分

比線程的單位更小——協程、纖程

它的本質就是一個線程的多個部分

在一個線程中能夠開啓不少協程

再執行程序的過程當中,遇到IO操做就凍結當前位置的狀態,而後去執行其餘的任務,在執行其餘任務過程當中,會不斷的檢測上一個凍結的任務是否IO結束,若是IO結束了就繼續從凍結的位置開始執行。

 

 

 

一個線程不會遇到阻塞,一直在使用CPU

多個線程---只能有一個線程使用CPU

協程比線程之間的切換和線程的建立銷燬所花費的時間,空間開銷都要小的多

 

 

總結協程的特色:

一、必須在只有一個單線程裏實現併發

二、修改享性數據不須要加鎖

三、用戶程序裏本身保存多個控制流的上下文棧

四、附加、一個協程遇到IO操做自動切換到其餘協程(如何實現檢測IO,yield,Greenleaf都沒法實現 就用到了gevent模塊(select機制))

 

 

Greenlet模塊

from greenlet import greenlet

def eat(name):
    print('%s eat 1' %name)
    g2.switch('egon')
    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('egon')#能夠在第一次switch時傳入參數,之後都不須要
greenlet實現狀態切換

 

單純的切換(在沒有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)) #10.985628366470337

#切換
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)) # 52.763017892837524
效率對比

 

greenlet只是提供了一種比generator更加便捷的切換方式,當切到一個任務執行時若是遇到io,那就原地阻塞,仍然是沒有解決遇到IO自動切換來提高效率的問題。

單線程裏的這20個任務的代碼一般會既有計算操做又有阻塞操做,咱們徹底能夠在執行任務1時遇到阻塞,就利用阻塞的時間去執行任務2。。。。如此,才能提升效率,這就用到了Gevent模塊。

 

 

Gevent模塊

 

安裝:pip3 install gevent

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

g1=gevent.spawn(func,1,,2,3,x=4,y=5)建立一個協程對象g1,spawn括號內第一個參數是函數名,如eat,後面能夠有多個參數,能夠是位置實參或關鍵字實參,都是傳給函數eat的

g2=gevent.spawn(func2)

g1.join() #等待g1結束

g2.join() #等待g2結束

#或者上述兩步合做一步:gevent.joinall([g1,g2])

g1.value#拿到func1的返回值
用法介紹
相關文章
相關標籤/搜索