Python asyncio簡單整理

前言

Python 為了避免產生 blocking 問題,替耗時的 IO、計算或網路相關操作提供了 asyncio,可以用並發的方式完成工作,同時最大限度地提高作業效率。

並發和並行的比較

正文

協程和任務(Coroutines and Tasks)

何謂協程?

協程指的是有宣告為 async 的函式

1
2
3
4
5
6
import asyncio

async def main():
print('hello')
await asyncio.sleep(1)
print('world')

此時main()就是協程,無法直接被執行

何謂任務?

協程可以被裝成任務,接續上面的程式碼

1
task = asyncio.create_task(main())

此時task會是一個任務,任務比起一般協程能做出更多流程控制。

協程和任務的使用

若要使用協程和任務,可使用await關鍵字

1
2
await task
await main()

在這邊要注意,await taskawait main()都會回傳main函式的執行結果,但是task是在create_task()沒多久後就執行了(會被丟進事件迴圈等待執行),後者則是執行await main()時才會被執行。

使用await的關鍵字,表示要 Python 等待後方任務或協程執行完畢再繼續執行下去。在等待執行完畢的期間,Python 會不斷切換工作執行,直到等待中的任務完成。

關於 task 的那些事(必看)

雖然asyncio.run()可以用來執行協程和任務,但它其實是用於 async 程式的進入點,因此在正常情況下,一個 async 只能有一個asyncio.run(),否則會引發錯誤

Queue Lock 與 Event

可以用來控制執行情況

Queue 佇列

先進先出

可以塞入需要處理的物件,在音樂機器人等需要有佇列時很有用

1
2
3
queue = asyncio.Queue()
await queue.put(Obj)
something = await queue.get()

Lock 鎖

能調控 IO 資源的取得,上鎖以後不能被使用,直到鎖被釋放為止,通常搭配async with使用

1
2
3
4
5
6
lock = asyncio.Lock()

#程式會等到鎖被釋放才進入該區塊

async with lock:
#access shared state

Event 事件

能調控程式是否要繼續向下執行(wait()),常用於 callback 函數中。 內部有個參數預設是false,可透過函數控制。當執行wait()時,若參數是true就會立刻繼續執行,否則會持續等待。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
async def waiter(event):
print('waiting for it ...')
await event.wait()
print('... got it!')

async def main():
# Create an Event object.
event = asyncio.Event()
# Spawn a Task to wait until 'event' is set.
waiter_task = asyncio.create_task(waiter(event))
# Sleep for 1 second and set the event.
await asyncio.sleep(1)
event.set()
# Wait until the waiter task is finished.
await waiter_task

asyncio.run(main())

對抗耗時的 IO 操作

面對耗時的 IO 操作,可以使用比較低層的操作方法

1
2
3
4
5
6
7
8
9
10
11
12
13
import asyncio

async def blocking_io():
#do something
asyncio.sleep(10)
return 'Fuck My Life'

async def main():
loop = asyncio.get_running_loop()
result = await loop.run_in_executor(None, blocking_io)
print(result)

asyncio.run(main())

用這種方法處理,主線程就不會卡住,在 Discord Bot 上常常用到。

結語

其實這篇是整理幾個重點給我自己看的,文中只有簡單帶過常用的東西,如果需要更深層的知識需要自己去搜尋。

有任何錯誤請通知我。