日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程语言 > python >内容正文

python

python3 携程_多任务(3):协程

發布時間:2025/3/15 python 32 豆豆
生活随笔 收集整理的這篇文章主要介紹了 python3 携程_多任务(3):协程 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

代碼環境:python3.6

上一篇文章我們講了 python 中多線程的使用:點擊閱讀,現在我們講講 python 中的協程。

異步IO

我們知道,CPU 速度遠遠快于磁盤、網絡等 IO。在 IO 編程中,假如一個 IO 操作阻塞了當前線程,會導致其他代碼無法執行,所以我們使用多線程或者多進程來并發執行代碼。

但是,系統資源是有限的,一旦線程數量過多,CPU 的時間就花在線程切換上了,真正執行代碼的時間下降,導致性能嚴重下降。

針對這個問題,我們需要另一種解決方法:異步 IO。

異步 IO,即當代碼需要執行一個耗時的 IO 操作時,它只發出 IO 指令,并不等待 IO 結果,然后就去執行其他代碼了。一段時間后,當 IO 返回結果時,再通知 CPU 進行處理。

python中最初的協程

了解最初的協程有助于我們理解后面現代協程的用法。

協程這個概念并不是 python 首次提出的,而是從其他語言借鑒過來的。

我們知道,兩個普通函數的調用是按順序的,比如A函數調用B函數,B執行完畢返回結果給A,A執行完畢。

協程看上去也是函數,如果協程A調用協程B,在執行過程中,協程B可以中斷,轉而執行A,再在適當的時候返回B接著從中斷處往下執行。

協程的這種執行特點,恰好符合我們的需求:通過協程實現異步 IO 編程。

生成器進化成協程

python 基于 generator 進行一系列功能改進后得到協程,語法上都是定義體中包含 yield 關鍵字。

在協程中,yield 不僅可以返回值,還能接收調用者通過.send()方法發出的參數。yield 通常出現在表達式右邊,如:data = yield something。如果 yield 后面沒有表達式,說明此時 yield 只負責接收數據,協程始終返回None。

簡單協程的基本行為

舉個簡單例子:

In [1]: def my_coroutine():

...: print('協程被激活')

...: while True:

# yield 后面不跟表達式,這里只接收 send() 傳過來的數據

...: x = yield

...: print(f'協程接收到參數:{x}')

...:

In [2]: my_corou = my_coroutine()

# 可查看協程當前狀態

In [3]: from inspect import getgeneratorstate

In [4]: getgeneratorstate(my_corou)

Out[4]: 'GEN_CREATED'

# 激活協程,此處可用 my_corou.send(None) 代替

In [5]: next(my_corou)

協程被激活

In [6]: getgeneratorstate(my_corou)

Out[6]: 'GEN_SUSPENDED'

In [7]: return_value = my_corou.send(99)

協程接收到參數:99

In [8]: print(return_value)

None

In [9]: my_corou.close()

In [10]: getgeneratorstate(my_corou)

Out[10]: 'GEN_CLOSED'

通過例子我們主要了解的是,協程需要手動激活才能真正調用,協程在不需要的時候要記得關閉。

用協程改進生產者-消費者模型

傳統生產者-消費者模型中,一個線程寫消息,另一個線程取消息,通過鎖機制控制隊列和等待,但是一不小心可能死鎖。

如果改用協程,生產者生產消息后,直接通過 yield 跳轉到消費者開始執行,待消費者執行完畢后,再切換回生產者繼續生產,整個流程無鎖且效率高:

from inspect import getgeneratorstate

def consumer():

r = '200 OK'

while True:

# yield接收生產者的數據賦值給n,并把處理結果狀態r返回

n = yield r

print(f'[CONSUMER] 消費了:{n}')

def producer(c):

# 別忘了激活協程

c.send(None)

n = 0

while n < 5:

n = n + 1

print(f'[PRODUCER] 生產了:{n}')

# 一旦生產了東西,通過c.send()切換到consumer執行

# consumer處理數據后通過yield返回結果狀態,這里獲取返回內容

r = c.send(n)

print(f'[PRODUCER] 消費者返回的處理結果:{r}')

print(f'生產者不生產了,看看當前consumer狀態:{getgeneratorstate(c)}')

c.close()

print(f'關閉consumer,看看當前consumer狀態:{getgeneratorstate(c)}')

if __name__ == "__main__":

producer(consumer())

上面例子整個流程只由一個線程執行且無鎖,生產者和消費者協作完成任務,這種屬于協作式多任務,跟多線程這種搶占式多任務要區分開。

asyncio

在 python3.4 版本中,開始引入標準庫asyncio直接內置了對異步 IO 的支持。

asyncio的編程模型就是一個消息循環。我們從asyncio模塊中直接獲取一個EventLoop的引用,然后把需要執行的協程扔到EventLoop中執行,就實現了異步 IO。

先簡單介紹下asyncio涉及到的一些詞語:

Future:一個對象,表示異步執行的操作。通常情況下自己不應該創建Future,而只能由并發框架如asyncio實例化。

Task:在EventLoop中負責執行協程的任務,是Future的子類。換句話說,Task就是Future,但反過來不一定。

下面是asyncio常用API:

asyncio.get_event_loop():獲取一個EventLoop對象,用來運行協程

asyncio.iscoroutine(obj):判斷一個對象是否是協程。

asyncio.sleep(delay):直接當做是一個耗時多少秒的協程即可。

asyncio.ensure_future(coro_or_future):入參是協程,則激活協程,返回一個Task對象;如果入參是Future,則將入參直接返回。

asyncio.gather(coros_or_futures):按入參中協程的順序保存協程的執行結果,大部分情況下使用。

asyncio.wait(futures):對比gather,不一定按入參順序返回執行結果。返回包含已完成和掛起的Task,可通過接收參數return_when選擇返回結果的時機,按實際情況使用。

我們將在下面結合新的關鍵字async/await來舉例說明。

async/await

為了簡化使用和標識異步 IO,從 python3.5 版本開始引入新的語法糖async/await,用async把一個generator標記為協程函數,然后在協程內部用await調用另一個協程實現異步操作。

注意:

用async標記協程函數,調用該函數時協程尚未激活,激活該函數可以用await或者yield from,也可以通過ensure_future()或者AbstractEventLoop.create_task()調度執行。

舉個例子:

from asyncio import sleep as aiosleep, gather, get_event_loop

async def compute(x, y):

print("計算 %s + %s ..." % (x, y))

await aiosleep(1)

return x + y

async def print_sum(x, y):

result = await compute(x, y)

print("%s + %s = %s" % (x, y, result))

async def coro_main():

'''一般我們會寫一個 coroutine 的 main 函數,專門負責管理協程'''

await gather(print_sum(1, 2), print_sum(4, 9))

def main():

aioloop = get_event_loop()

# 內部使用ensure_future()激活協程

aioloop.run_until_complete(coro_main())

aioloop.close()

if __name__ == "__main__":

main()

執行結果:

計算 1 + 2 ...

計算 4 + 9 ...

(暫停約1秒,實際輸出沒有這行)

1 + 2 = 3

4 + 9 = 13

觀察例子運行結果,我們看到:

當協程開始計算1+2前還有一個耗時 1 秒的 IO 操作,當前線程并未等待,而是去執行其他協程計算4+9,實現了并發執行。

協程結果按gather入參的順序打印。

總結

面對 CPU 高速執行和 IO 設備的龜速嚴重不匹配問題,我們至少要知道兩種解決方法:使用多進程和多線程并發執行代碼;使用異步 IO 執行代碼。

python 協程是基于生成器改進后得到的,底部實現都是定義體中包含yield關鍵字。

協程屬于協作式多任務,整個流程無需鎖,跟多線程這種搶占式多任務要區分開。

asyncio支持異步 IO,我們從asyncio模塊中直接獲取一個EventLoop的引用,然后把需要執行的協程扔到EventLoop中執行,就實現了異步 IO。

定義協程函數時,我們用async標記協程函數,然后在協程內部用await調用另一個協程實現異步操作。

總結

以上是生活随笔為你收集整理的python3 携程_多任务(3):协程的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。