asyncio 高階API列表
asyncio中函數可以分爲高階函數和低階函數。低階函數用於調用事件循環、linux 套接字、信號等更底層的功能,高階函數是屏蔽了更多底層細節的任務併發,任務執行函數。通常開發中使用更多的是高階函數。本篇主要介紹asyncio中常用的高階函數。
由於asyncio在不同的版本中有差異,本文以及本系列都以python3.10爲準。
函數 | 功能 |
---|---|
run() | 創建事件循環,運行一個協程,關閉事件循環。 |
create_task() | 創建一個asyncio的Task對象 |
await sleep() | 休眠幾秒 |
await gather() | 併發執行所有事件的調度和等待 |
await wait_for() | 有超時控制的運行 |
await shield() | 屏蔽取消操作 |
await wait() | 完成情況的監控器 |
current_task() | 返回當前Task對象 |
all_tasks() | 返回事件循環中所有的task對象 |
Task | Task對象 |
to_thread() | 在不同的 OS 線程中異步地運行一個函數 |
run_coroutine_threadsafe() | 從其他OS線程中調度一個協程 |
for in as_completed() | 用 for 循環監控完成情況 |
run
函數原型:
asyncio.run(coro, *, debug=False)
功能:創建事件循環,運行傳入的協程。該函數總是會創建一個新的事件循環並在結束時關閉它,應該被當做asyncio程序的主入口點。run() 函數是用來創建事件,將task加入事件,運行事件的函數。
async def main():
await asyncio.sleep(1)
print('hello')
asyncio.run(main())
run() 從功能上等價於以下低階API。獲取一個事件循環,創建一個task,加入事件循環。
loop = asyncio.get_event_loop()
task = loop.create_task(main())
loop.run_until_complete(task)
create_task
函數原型:
asyncio.create_task(coro, *, name=None)
功能:將協程函數封裝成一個Task。協程函數沒有生命週期,但是Task有生命週期。
將協程打包爲一個 Task 並自動尋找事件循環加入。返回 Task 對象。該任務會在 get_running_loop() 返回的循環中執行,如果當前線程沒有在運行的循環則會引發 RuntimeError。
async def coro():
await asyncio.sleep(1)
print("i am coro")
async def main():
task = asyncio.create_task(coro())
print(f"task狀態:{task._state}")
await asyncio.sleep(2)
print(f"task狀態:{task._state}")
print("i am main")
asyncio.run(main())
結果:
task狀態:PENDING
i am coro
task狀態:FINISHED
i am main
結果分析:
可以看到task運行中的狀態和結束的生命週期狀態
gather
函數原型:
asyncio.gather(*aws, return_exceptions=False)
功能:
併發執行所有可等待對象,收集任務結果,返回所有已經完成的task的結果。結果將是一個由所有返回值組成的列表。結果值的順序與傳入的task的順序一致。可等待對象可以是協程和task。
如果序列中是協程而不是task,那麼會將其自動封裝成task加入事件循環。
import asyncio
async def coro(value):
print(f"hello coro{value}")
return f"coro{value}"
async def main():
tasks = [coro(i) for i in range(5)]
res = await asyncio.gather(*tasks)
for i in res:
print(i)
asyncio.run(main())
結果:
hello coro0
hello coro1
hello coro2
hello coro3
hello coro4
coro0
coro1
coro2
coro3
coro4
結果分析:
獲取了所有協程的返回值,並且返回的順序和任務的順序一致。
wait
函數原型:
asyncio.wait(aws, *, timeout=None, return_when=ALL_COMPLETED)
功能:
併發地運行序列中的可等待對象,並進入阻塞狀態直到滿足 return_when 所指定的條件。將task任務結果收集起來,返回兩個 Task/Future 集合: (done, pending)。done是已經完成的任務,pending是未完成的任務,未完成的原因可能是超時或return_when策略。
aws:
aws中保存的是task而不是協程,從3.8起不建議傳入協程,3.11將不再支持傳入協程。
timeout:
如指定 timeout (float 或 int 類型) 則它將被用於控制返回之前等待的最長秒數。
請注意此函數不會引發 asyncio.TimeoutError。當超時發生時,未完成的 Future 或 Task 將不會繼續執行,不會返回結果。
return_when:
return_when 指定此函數應在何時返回。它必須爲以下參數之一:
參數 | 描述 |
---|---|
FIRST_COMPLETED | 函數將在任意可等待對象結束或取消時返回。 |
FIRST_EXCEPTION | 函數將在任意可等待對象因引發異常而結束時返回。當沒有引發任何異常時它就相當於 ALL_COMPLETED。 |
ALL_COMPLETED | 函數將在所有可等待對象結束或取消時返回。 |
基礎使用示例
:
import asyncio
async def coro(value):
print(f"hello coro{value}")
return f"coro{value}"
async def main():
tasks = [asyncio.create_task(coro(i)) for i in range(5)]
done, pending = await asyncio.wait(tasks)
for i in done:
print(i.result())
asyncio.run(main())
結果:
hello coro0
hello coro1
hello coro2
hello coro3
hello coro4
coro1
coro2
coro0
coro3
coro4
結果分析:
返回結果和執行順序並不是一致的
指定超時時間
:
import asyncio
from asyncio import FIRST_COMPLETED
async def coro(value):
print(f"hello coro{value}")
await asyncio.sleep(value)
return f"coro{value}"
async def main():
tasks = [asyncio.create_task(coro(i)) for i in range(5)]
done, pending = await asyncio.wait(tasks, timeout=3)
print("---------finish----------")
for i in done:
print(i.result())
print("---------pending----------")
for i in pending:
print(i)
asyncio.run(main())
hello coro0
hello coro1
hello coro2
hello coro3
hello coro4
---------finish----------
coro1
coro2
coro0
---------pending----------
<Task pending name='Task-5' coro=<coro() running at /Users/ljk/Documents/code/daily_dev/async_demo/wait_demo.py:6> wait_for=<Future finished result=None>>
<Task pending name='Task-6' coro=<coro() running at /Users/ljk/Documents/code/daily_dev/async_demo/wait_demo.py:6> wait_for=<Future pending cb=[Task.task_wakeup()]>>
結果分析:
超時未完成的task會保存在pending中,未完成的task在超時之後不會繼續執行,沒有返回結果。
return_when配置任意任務完成就返回
:
import asyncio
from asyncio import FIRST_COMPLETED
async def coro(value):
print(f"hello coro{value}")
await asyncio.sleep(value)
return f"coro{value}"
async def main():
tasks = [asyncio.create_task(coro(i)) for i in range(5)]
done, pending = await asyncio.wait(tasks, return_when=FIRST_COMPLETED)
print("---------finish----------")
for i in done:
print(i.result())
print("---------pending----------")
for i in pending:
print(i)
asyncio.run(main())
結果:
hello coro0
hello coro1
hello coro2
hello coro3
hello coro4
---------finish----------
coro0
---------pending----------
<Task pending name='Task-5' coro=<coro() running at /Users/ljk/Documents/code/daily_dev/async_demo/wait_demo.py:6> wait_for=<Future pending cb=[Task.task_wakeup()]>>
<Task pending name='Task-3' coro=<coro() running at /Users/ljk/Documents/code/daily_dev/async_demo/wait_demo.py:6> wait_for=<Future pending cb=[Task.task_wakeup()]>>
<Task pending name='Task-4' coro=<coro() running at /Users/ljk/Documents/code/daily_dev/async_demo/wait_demo.py:6> wait_for=<Future pending cb=[Task.task_wakeup()]>>
<Task pending name='Task-6' coro=<coro() running at /Users/ljk/Documents/code/daily_dev/async_demo/wait_demo.py:6> wait_for=<Future pending cb=[Task.task_wakeup()]>>
結果分析:
獲取到任意結果就返回,未完成的task保存在pending中。未完成的task在超時之後不會繼續執行。
as_completed
函數原型:
asyncio.as_completed(aws, *, timeout=None)
說明:併發執行aws中保存的可等待對象,返回一個協程的迭代器。可以從迭代器中取出最先執行完成的task的結果。返回結果和執行順序不一致。aws中可以是task或協程序列。
import asyncio
async def coro(value):
print(f"hello coro{value}")
return f"coro{value}"
async def main():
tasks = [coro(i) for i in range(5)]
for item in asyncio.as_completed(tasks):
res = await item
print(res)
asyncio.run(main())
結果:
hello coro2
hello coro3
hello coro4
hello coro1
hello coro0
coro2
coro3
coro4
coro1
coro0
結果分析:
所有任務都會執行完成,沒有超時配置。返回順序和執行順序無關。
gather、wait、as_completed 異同點小結
asyncio協程體系中可以實現創建多個任務併發執行的函數有以下三個:
- asyncio.gather
- asyncio.wait
- asyncio.as_completed
不同之處比較:
特性/函數 | gather | wait | as_completed |
---|---|---|---|
入參 | 同時支持task和協程序列 | 只支持task序列 | 同時支持task和協程序列 |
獲取結果順序 | 有序,和併發序列順序相同 | 無序,和併發序列無關 | 無序,和併發序列無關 |
返回 | 返回結果列表,保存的是函數返回值。 | 返回元組done、pending。元組中保存的是task,而非task 的函數返回值 | 返回一個迭代器,從中可迭代出函數返回值。 |
wait for
函數原型:
asyncio.wait_for(aw, timeout)
功能:執行單個可等待對象,指定 timeout 秒數後超時
等待可等待對象完成,指定timeout秒數後超時。和gather類似,可以自動將協程轉化成任務加入循環。
timeout 可以爲 None,也可以爲 float 或 int 型數值表示的等待秒數。如果 timeout 爲 None,則等待直到完成。
如果發生超時,任務將取消並引發 asyncio.TimeoutError。
async def coro():
# 睡眠5s
await asyncio.sleep(3600)
print('finish!')
async def main():
# Wait for at most 1 second
try:
await asyncio.wait_for(coro(), timeout=1.0)
except asyncio.TimeoutError:
print('timeout!')
asyncio.run(main())
結果:
timeout!
高階API中常用的函數基本就是這些,下一篇分析低階函數。
連載一系列關於python異步編程的文章。包括同異步框架性能對比、異步事情驅動原理等。歡迎關注微信公衆號第一時間接收推送的文章。