非同步IO Python中的協程

2021-08-13 22:47:06 字數 3875 閱讀 2265

python的協程基礎在於生成器,生成器可以在我們需要的時候執行運算,得出執行結果,構造乙個生成器有兩種方法。

一是使用():(x * x for x in range(10)) 列表使用中括號,生成器使用小括號

二是在函式中使用yield,此時該函式就不是乙個函式,而是乙個生成器物件

我們主要使用第二種方法來定義乙個生成器,接下來我介紹對於yield, yield from 以及python3.5的新語法async/await

我以乙個簡單的例子來介紹yield語法:

def

test

(times):

return_info = ''

n = 0

while n < times:

n += 1

recv = yield n

print("生成器收到資料%s" % recv)

print("生成器結束")

generator = test(5)

a=generator.send(none)

print(a)

b=generator.send(2)

print(b)

c=generator.send(none)

print(c)

d=generator.send(none)

print(d)

e=generator.send(none)

print(e)

def

test

(times):

return_info = ''

n = 0

while n < times:

n += 1

recv = yield n

print("生成器收到資料%s" % recv)

print("生成器結束")

generator = test(5)

a=generator.send(none)

print(a)

b=generator.send(2)

print(b)

c=generator.send(none)

print(c)

d=generator.send(none)

print(d)

e=generator.send(none)

print(e)

上述**最主要的部分在於 

recv = yield n

,在等號左邊和後邊將**塊分成了兩部分。

當我們啟動生成器之後,**會執行到yield n 後中斷,並將yield 之後的表示式的值返回給generator.send(「要傳送的資料」),也就是a = generator.send(「none」) 此時a == n。

當我們再次通過send(「none」)啟動生成器時,會從上次中斷的地方開始重新啟動,也就是從等號的左邊開始執行,將send(「none」)中的none賦值給recv,然後繼續執行**直到下一次的yield n後中斷。

我們需要注意的一點是,當我們生成乙個生成器generator之後,我們使用generator.send(none)來啟動生成器,此時generator.send(none)相當於next(generator),第一次執行generator時必須send(none),因為根據yield語法,第一次執行生成器會在yield n 處中斷,此時recv沒有接受值,所以只能為none,這一部分在後面了解了yield的執行機制後就會明白,下面簡單畫乙個圖

yield from可以很方便地讓我們呼叫另乙個generator,yield from 建立了使用者端到內層generator之間的」通訊」,也可以方便我們在內層generator中處理異常。我們可以看以下**:

def

inner

(): coef = 1

total = 0

while

true:

input_val = yield total

if input_val is

2: print('break')

break

total = total + coef * input_val

print("inner end")

defouter2

(): print("before inner(), i do this.")

a = yield

from inner()

print(a)

print("after inner(), i do that.")

c = outer2()

a = c.send(none)

print(a)

print("first send end")

b = c.send(2)

print(b)

輸出結果為

before inner(), i do this.

0first send end

break

inner

endnone

after

inner(), i do that.

當我們啟動生成器時,**會執行到yield from inner(),然後進入內層的generator,執行到yield total,此次**執行結束,進入中斷。

當我們第二次啟動生成器時,c.send(2)會將2直接傳遞給內層的input_val,然後執行內層的yield,列印出』break』,內層的yield結束,列印出』inner end』,由於此時是中斷,沒有yield,即沒有返回值,那麼yield from inner()的返回值為none,列印出a為none,最後列印結束語句,退出程式。

值得一提的是,執行完b = c.send(

2)語句,也就是實際上執行完函式outer2()裡面的print(

"after inner(), i do that."

)後,退出函式執行後,最後的main函式的print(b)是沒

有執行的,換成什麼語句都不會再執行,可能是因為這個是yield語句,沒有返回值會直接中斷

此時我們可以看出,yield from會將send的值傳遞到最裡層,再次啟動生成器也是從最裡層的yield開始啟動,然後當內層的yield結束後執行外層的yield直到程式結束。我們可以畫乙個示例圖幫助理解

async和await是python3.5之後的新語法,可以用來替換@asyncio.coroutine和await。

async可以將乙個generator標記為coroutine型別,然後將其放入event_loop中執行,當我們在event_loop中放入多個coroutine的generator後,我們啟動事件迴圈,此時遇到await,該generator就會中斷,繼續執行下乙個generator,直到所有的generator的await執行完成後,event_loop再開始下一次訊息迴圈,此時會再次啟動event_loop中的生成器。

舉乙個形象地例子就是你在快餐店排隊,每乙個人在收銀台付款後(await money),到取餐處等待,直到每個人都付款之後,開始打包外賣,誰的先做好就可以拿走,而不是根據付款的順序來取餐。

關於非同步io在伺服器程式設計方面用到的地方很多,了解了基本的特性後,還需要大量的實際程式設計經驗才能真正掌握python中協程的概念,繼續去填坑了0.0。。。

非同步IO Python中的協程

我們在實際的問題中會遇到一些堵塞執行緒的場景,比如ui程式進行耗時的計算阻塞主線程 進行io操作,比如讀寫文件或者網路傳輸時會阻塞執行緒等等,之前我採用的是使用多程序 多執行緒的處理方式,但這種方式相比於協程來說都比較慢,特別是在windows作業系統下啟動乙個程序的開銷是巨大的,因此掌握pytho...

python協程與非同步協程

在前面幾個部落格中我們一一對應解決了消費者消費的速度跟不上生產者,浪費我們大量的時間去等待的問題,在這裡,針對業務邏輯比較耗時間的問題,我們還有除了多程序之外更優的解決方式,那就是協程和非同步協程。在引入這個概念之前我們先看 看這個圖 從這個我們可以看出來,假如來了9個任務,即使我們開了多程序,在業...

非同步I O 協程

什麼是協程呢?協程 coroutines 是一種比執行緒更加輕量級的存在,正如乙個程序可以擁有多個執行緒一樣,乙個執行緒可以擁有多個協程。協程不是被作業系統核心所管理的,而是完全由程式所控制,也就是在使用者態執行。這樣帶來的好處是效能大幅度的提公升,因為不會像執行緒切換那樣消耗資源。協程不是程序也不...