Python高階之協程詳解

2022-10-08 05:03:08 字數 3123 閱讀 5366

目錄

協程(co-routine,又稱微執行緒)是一種多方協同的工作方式。當前執行者在某個時刻主動讓出(yield)控制流,並記住自身當前的狀態,以便在控制流返回時能從上次讓出的位置恢復(resume)執行。

簡而言之,協程的核心思想就在於執行者對控制流的 「主動讓出」 和 「恢復」。相對於,執行緒此類的 「搶占式排程」 而言,協程是一種 「協作式排程」 方式。

在 i/o 密集型場景中,搶占式排程的解決方案是 「非同步 + **」 機制。

其存在的問題是,在某些場景中會使得整個程式的可讀性非常差。以**為例,服務中臺提供了非同步介面,發起者請求之後立即返回,服務此時給了發起者乙個唯一標識 id,等服務完成**後把結果放到乙個訊息佇列,此時需要發起者不斷消費這個 mq 才能拿到**是否完成的結果。

可見,整體的邏輯被拆分為了好幾個部分,各個子部分都會存在狀態的遷移,日後必然是 bug 的高發地。

而隨著網路技術的發展和高併發要求,協程所能夠提供的使用者態協同排程機制的優勢,在網路操作、檔案操作、資料庫操作www.cppcns.com、訊息佇列操作等重 i/o 操作場景中逐漸被挖掘。

協程將 i/o 的處理權從核心態的作業系統交還給使用者態的程式自身。使用者態程式在執行 i/o 時,主動的通過 yield(讓出)cpu 的執行權給其他協程,多個協程之間處於平等、對稱、合作的關係。

當程式執行時,作業系統會為每個程式分配一塊同等大小的虛擬記憶體空間,並將程式的**和所有靜態資料載入到其中。然後,建立和初始化 stack 儲存,用於儲存程式的區域性變數,函式引數和返回位址;建立和初始化 heap 記憶體;建立和初始化 i/o 相關的任務。當前期準備工作完成後,作業系統將 cpu 的控制權移交給新建立的程序,程序開始執行。

乙個程序可以有乙個或多個執行緒,同一程序中的多個執行緒將共享該程序中的全部系統資源,如:虛擬位址空間,檔案描述符和訊號處理等等。但同一程序中的多個執行緒有各自的呼叫棧和執行緒本地儲存。

協程是一種比執行緒更加輕量級的存在,協程不是被作業系統核心所管理,而完全是由使用者態程式所控制。協程與執行緒以及程序的關係如下圖所示。可見,協程自身無法利用多核,需要配合程序來使用才可以在多核平台上發揮作用。

協程通過 「掛起點」 來主動 yield(讓出)cpu,並儲存自身的狀態,等候恢復。例如:首先在 funca 函式中執行,執行一段時間後呼叫協程,協程開始執行,直到第乙個掛起點,此後就像普通函式一樣返回 funca 函式。 funca 函式執行一些**後再次呼叫該協程,注意,協程這時就和普通函式不一樣了。協程並不是從第一條指令開始執行而是從上一次的掛起點開始執行,執行一段時間後遇到第二個掛起點,這時協程再次像普通函式一樣返回 funca 函式,funca 函式執行一段時間後整個程式結束。

可見,協程之所可以能夠 「主動讓出」 和 「被恢復」,是解析器在函式執行時堆疊中儲存了其執行的 context(上下文)。

python 對協程的支援經歷了多個版本:

async/await 的示例程式:

'ht程式設計客棧tps:',

'']async def download_image_async(session, dir, img_url):

download_path = dir / os.path.basename(img_url)

async with session.get(img_url) as response:

with download_path.open('wb') as f:

while true:

# 在 async 函式中使用 await 關鍵字表示等待 task 執行完成,也就是等待 yeild 讓出控制權。

# 同時,asyncio 使用事件迴圈 even程式設計客棧t_loop 來實現整個過程。

chunk = await response.content.read(512)

if not chunk:

break

f.write(chunk)

logger.info('downloaded: ' + i

# 使用 async 關鍵字宣告乙個非同步/協程函式。

# 呼叫該函式時,並不會立即執行,而是返回乙個協程物件,後續在 event_loop 中執行。

# event_loop 事件迴圈充當管理者的角色,將控制權在幾個協程函式之間切換。

event_loop = asyncio.get_event_loop()

try:

程式設計客棧 event_loop.run_until_complete(main())

finally:

event_loop.close()

logger.info('download time: %s seconds', time() - start)

本文標題: python高階之協程詳解

本文位址:

Python 高階 之 協程

協程的概念級描述 與執行緒對比 知乎 鏈結 執行緒有兩個必須要處理的問題 一是碰著阻塞式i o會導致整個程序被掛起 二是由於缺乏時鐘阻塞,程序需要自己擁有排程執行緒的能力。如果一種實現使得每個執行緒需要自己通過呼叫某個方法,主動交出控制權。那麼我們就稱這種執行緒是協作式的,即是協程。在python中...

python高階 協程

def demo1 for i in range 3 print 正在執行demo1 format i yield defdemo2 for i in range 3 print 正在執行demo2 format i yield g1 demo1 g2 demo2 print g1 while tr...

python基礎 協程詳解

python的yield不但可以返回乙個值,它還可以接收呼叫者發出的引數。來看例子 傳統的生產者 消費者模型是乙個執行緒寫訊息,乙個執行緒取訊息,通過鎖機制控制佇列和等待,但一不小心就可能死鎖。如果改用協程,生產者生產訊息後,直接通過yield跳轉到消費者開始執行,待消費者執行完畢後,切換回生產者繼...