協程(coroutine)是由程式設計師在**中顯示排程的。(程序和執行緒是由作業系統排程,執行緒是作業系統排程的最小單位)。
看過前邊的文章應該知道,執行緒任務在 io 阻塞之後,作業系統會進行執行緒切換,這個切換會浪費時間與資源。而協程是在單個執行緒中併發的多個任務,當執行中的協程任務遇到阻塞之後,立即切換到其他就緒狀態協程任務去執行,這樣會極大的減小了執行緒因為阻塞而有作業系統切換。協程的切換屬於程式級別,非常迅速,上線文完全有程式設計師在**中顯示管理,切換速度極快,所以減少了執行緒切換浪費資源。
協程的優點:
協程的缺點:
yield 將乙個函式構造為生成器。使用 yield 可以做協程的簡單實現。當在任務中呼叫生成器的時,當前執行任務會切換到生成器來獲取資料,獲取到資料之後再切回之前的執行任務。
def
consumer()
:while
true
:print
('馬上執行到yield'
) x =
yield
print
('處理了資料:'
, x)
if __name__ ==
"__main__"
: c = consumer(
)next
(c)# 第一次執行到 yield
next
(c)# 執行 yield 之後,直到下一次遇到 yield
c.send(1)
# 向 yield 傳送1,向後執行直到下一次遇到 yield
c.send(2)
# 向 yield 傳送2,向後執行直到下一次遇到 yield
執行結果:
馬上執行到yield當然出了以上的用法 還有 yield 正常一點的生成器用法:處理了資料: none
馬上執行到yield
處理了資料: 1
馬上執行到yield
處理了資料: 2
馬上執行到yield
def
consumer()
:for i in
range(6
):yield i
if __name__ ==
"__main__"
:for i in consumer():
print
(i**2)
# 或者使用 next
# 生成器不可以二次使用
c = consumer(
)print
(next
(c))
print
(next
(c))
print
(next
(c))
# 生成器中已經使用完畢,下乙個報錯了
print
(next
(c))
執行結果:
greenlet 提供了 python 中協程實現的一種。最外層是呼叫的初始任務,內層任務是各個 greenlet。可以建立協程並在它們之間跳轉。
在 greenlet 協程中跳轉都是顯式的:乙個 greenlet(叫它g1)選擇跳轉到另乙個 greenlet(叫它g2),這將導致 g1 暫停,而 g2 開始執行一直到遇到切換或執行完畢,當執行完畢時會自動切換到 g1,g1 繼續執行(切換走的時候又狀態儲存)。
在 greenlet 中切換到協程需要使用 switch 方法,當對 greenlet 第一次執行 greenlet.switch() 時,需要傳遞引數,在後續的 switch 中不用傳參(傳了也無效)
from greenlet import greenlet
# 協程切換之 吃完睡 睡醒吃 吃完再睡
defeat
(user)
:print
(% user)
g2.switch(
'dolphin'
)# 3
print
('%s eat banane'
% user)
g2.switch(
)def
sleep
(user)
:print
('%s is sleeping 1'
% user)
g1.switch(
"jary"
)print
('%s is sleeping 2'
% user)
if __name__ ==
"__main__"
: g1 = greenlet(eat)
g2 = greenlet(sleep)
g1.switch(
'dolphin'
)
輸出結果:
greenlet 相比較 generator 提供了 switch 更方便切換協程,但是所有的協程都由我們手動排程還是不夠友好。如果能在協程之間遇到阻塞自動排程,那可就太好了~
於是,gevent 乘著七彩祥雲來了~
gevent 是乙個包裝了 greenlet 的第三方庫,他使用起來更像 threading 那樣可以協程也實現自動排程,可以輕鬆通過 gevent 實現併發同步或非同步程式設計。
使用 gevent.spawn 啟動協程不同於 greenlet 的手動切換。使用 gevent.spawn 建立的協程,會進入就緒狀態等待執行。
對於已經常見的協程,可能主線程已經結束其還未執行完畢,可以在主線程中使用 join 等待期執行完畢,或者使用 gevent.joinall() 等待多個協程 。
在 gevent 協程中,預設只認 gevent 定義的阻塞(例如:gevent.sleep())。像 time.sleep()、open()、網路請求這種阻塞會被忽略。可以通過打補丁來解決:
from gevent import monkey
# 在其之後的 time.sleep()、open()、網路請求 都被 gevent 認為會阻塞
monkey.patch_all(
)# 請把這種方式當成一種習慣
from gevent import monkey;monkey.patch_all(
)
python協程使用 協程的案例
概念 使用者層面在乙個執行緒中進行多工切換的機制,比執行緒更加輕量級 實現併發量更大 協程的使用 使用第三方庫 gevent gevent 是乙個基於協程的 python 網路庫,在遇到 io 阻塞時,程式會自動進行切換,可以讓我們用同步的放肆寫非同步 io 協程的使用 from gevent im...
python協程與非同步協程
在前面幾個部落格中我們一一對應解決了消費者消費的速度跟不上生產者,浪費我們大量的時間去等待的問題,在這裡,針對業務邏輯比較耗時間的問題,我們還有除了多程序之外更優的解決方式,那就是協程和非同步協程。在引入這個概念之前我們先看 看這個圖 從這個我們可以看出來,假如來了9個任務,即使我們開了多程序,在業...
python 併發程式設計 協程 協程介紹
協程 是單執行緒下的併發,又稱微執行緒,纖程。英文名coroutine。一句話說明什麼是執行緒 協程是一種使用者態的輕量級執行緒,即協程是由使用者程式自己控制排程的 需要強調的是 1.python的執行緒屬於核心級別的,即由作業系統控制排程 如單執行緒遇到io或執行時間過長就會被迫交出cpu執行許可...