asyncio非同步IO--協程(Coroutine)與任務(Task)詳解
摘要:本文翻譯自Coroutines and Tasks,主要介紹asyncio中用於處理協程和任務的方法和介面。在翻譯過程中,譯者在官方文件的基礎上增加了部分樣例程式碼和示意圖表,以幫助讀者對文件的理解。本文內容主要針對python3.7,在低版本的python中可能不適用,敬請留意。原創內容,如需轉載請註明出處。
譯者:馬鳴謙(郵箱:[email protected])
協程
協程(coroutines)是通過async/await
定義函式或方法,是使用asyncio進行非同步程式設計的首選途徑。如下,是一個協程的例子:
import asyncio async def main(): print("hello") await asyncio.sleep(1) print("world")
上例中的 main
方法就是我們定義的協程
。
我們在互動環境(Python3.7)下執行以上程式碼,看看效果:
>>> import asyncio
>>> async def main():
... print("hello")
... await asyncio.sleep(1)
... print("world")
>>> asyncio.run(main())
hello
world
需要注意的是:如果像執行普通程式碼一樣直接呼叫main()
,只會返回一個coroutine
物件,main()
方法內的程式碼不會執行:
>>> main() #直接執行`main()`返回的是一個`coroutine物件`。
<coroutine object main at 0x0000000002C97848>
實際上,asyncio提供了三種執行協程
的機制:
- 使用
asyncio.run()
執行協程。一般用於執行最頂層的入口函式,如main()
。 await
一個協程
。一般用於在一個協程
中呼叫另一協程
。 如下是一個示例:
>>> import time >>> async def say_after(delay,what): await asyncio.sleep(delay) print(what) >>> async def main(): print(f"started at {time.strftime('%X')}") await say_after(1,"hello") await say_after(2,"world") print(f"finished at {time.strftime('%X')}") >>> asyncio.run(main()) started at 16:47:10 hello world finished at 16:47:13
執行耗時 3秒
- 用
asyncio.create_task()
方法將Coroutine(協程)
封裝為Task(任務)
。一般用於實現非同步併發操作。 需要注意的是,只有在當前執行緒存在事件迴圈的時候才能建立任務(Task)。
我們修改以上的例程,併發執行 兩個say_after
協程。
async def main():
task1 = asyncio.create_task(say_after(1,"hello"))
task2 = asyncio.create_task(say_after(2,"world"))
print(f"started at {time.strftime('%X')}")
await task1
await task2
print(f"finished at {time.strftime('%X')}")
執行asyncio.run(main())
,結果如下:
started at 17:01:34
hello
world
finished at 17:01:36
耗時2秒
“可等待”物件(Awaitables)
如果一個物件能夠被用在await
表示式中,那麼我們稱這個物件是可等待物件(awaitable object)
。很多asyncio API
都被設計成了可等待的
。
主要有三類可等待物件:
- 協程
coroutine
- 任務
Task
- 未來物件
Future
。
Coroutine(協程)
Python的協程
是可等待的(awaitable)
,因此能夠被其他協程
用在await
表示式中。
import asyncio
async def nested():
print("something")
async def main():
# 如果直接呼叫 "nested()",什麼都不會發生.
# 直接呼叫的時候只是建立了一個 協程物件 ,但這個物件沒有被 await,
# 所以它並不會執行.
nested()
# 那麼我們 await 這個協程,看看會是什麼結果:
await nested() # 將會列印 "something".
asyncio.run(main())
重要:在這篇文章中,術語coroutine
或協程
指代兩個關係緊密的概念:
協程函式(coroutine function)
:由async def
定義的函式;協程物件(coroutine object)
:呼叫協程函式
返回的物件。
asyncio也支援傳統的基於生成器的協程。
Task(任務)
Task
用來 併發的 排程協程。
當一個協程
通過類似 asyncio.create_task()
的函式被封裝進一個 Task
時,這個協程
會很快被自動排程執行:
import asyncio
async def nested():
return 42
async def main():
# Schedule nested() to run soon concurrently
# with "main()".
task = asyncio.create_task(nested())
# "task" can now be used to cancel "nested()", or
# can simply be awaited to wait until it is complete:
await task
asyncio.run(main())
Future(未來物件)
Future
是一種特殊的 底層 可等待物件,代表一個非同步操作的最終結果。
當一個Future
物件被await
的時候,表示當前的協程會持續等待,直到 Future
物件所指向的非同步操作執行完畢。
在asyncio中,Future
物件能使基於回撥的程式碼被用於asyn/await
表示式中。
一般情況下,在應用層程式設計中,沒有必要 建立Future
物件。
有時候,有些Future
物件會被一些庫和asyncio API暴露出來,我們可以await
它們:
async def main():
await function_that_returns_a_future_object()
# this is also valid:
await asyncio.gather(
function_that_returns_a_future_object(),
some_python_coroutine()
)
執行asyncio程式
asyncio.run(coro, * , debug=False)
這個函式執行coro
引數指定的 協程
,負責 管理asyncio事件迴圈 , 終止非同步生成器。
在同一個執行緒中,當已經有asyncio事件迴圈在執行時,不能呼叫此函式。
如果debug=True
,事件迴圈將執行在 除錯模式。
此函式總是建立一個新的事件迴圈,並在最後關閉它。建議將它用作asyncio程式的主入口,並且只調用一次。
Python3.7新增
重要:這個函式是在Python3.7被臨時新增到asyncio中的。
建立Task
asyncio.create_task(coro)
將coro
引數指定的協程(coroutine)
封裝到一個Task
中,並排程執行。返回值是一個Task
物件。
任務在由get_running_loop()
返回的事件迴圈(loop)中執行。如果當前執行緒中沒有正在執行的事件迴圈,將會引發RuntimeError
異常:
import asyncio
async def coro_1():
print("do somthing")
task = asyncio.create_task(coro_1())
因為當前執行緒中沒有正執行的事件迴圈,所以引發異常:
Traceback (most recent call last):
File "C:\Program Files\Python37\lib\site-packages\IPython\core\interactiveshell.py", line 3265, in run_code
exec(code_obj, self.user_global_ns, self.user_ns)
File "<ipython-input-4-456c15a4ed16>", line 1, in <module>
task = asyncio.create_task(coro_1())
File "C:\Program Files\Python37\lib\asyncio\tasks.py", line 324, in create_task
loop = events.get_running_loop()
RuntimeError: no running event loop
對以上程式碼稍作修改,建立main()
方法,在其中建立Task
物件,然後在主程式中利用asyncio.run()
建立事件迴圈
:
import asyncio
async def coro():
print("something is running")
async def main():
task = asyncio.create_task(coro())
print(asyncio.get_running_loop())
asyncio.run(main())
執行結果如下:
<_WindowsSelectorEventLoop running=True closed=False debug=False>
something is running
此函式已經被引入到Python3.7。在Python早期版本中,可以使用底層函式asyncio.ensure_future()
代替。
async def coro():
...
# In Python 3.7+
task = asyncio.create_task(coro())
...
# This works in all Python versions but is less readable
task = asyncio.ensure_future(coro())
...
Python3.7新增
Sleeping
coroutine asyncio.sleep(delay,result=None,* ,loop=None)
阻塞delay
秒,例如delay=3
,則阻塞3秒。
如果指定了result
引數的值
,則在協程結束時,將該值
返回給呼叫者。
sleep()
通常只暫停當前task
,並不影響其他task
的執行。
不建議使用loop
引數,因為Python計劃在3.10
版本中移除它。
以下是一個協程的例子,功能是在5秒鐘內,每秒顯示一次當前的日期
:
import asyncio
import datetime
async def display_date():
loop = asyncio.get_running_loop()
end_time = loop.time() + 5.0
while True:
print(datetime.datetime.now())
if (loop.time() + 1.0) >= end_time:
break
await asyncio.sleep(1)
asyncio.run(display_date())
執行結果大致如下:
2018-11-20 11:27:15.961830
2018-11-20 11:27:16.961887
2018-11-20 11:27:17.961944
2018-11-20 11:27:18.962001
2018-11-20 11:27:19.962059
2018-11-20 11:27:20.962116
併發執行Tasks
awaitable asyncio.gather(* aws, loop=None, return_exceptions=False)
併發執行aws
引數指定的 可等待(awaitable)物件
序列。
如果 aws
序列中的某個 awaitable 物件
是一個 協程
,則自動將這個 協程
封裝為 Task
物件進行處理。例如:
import asyncio
async def factorial(name, number):
f = 1
for i in range(2, number + 1):
print(f"Task {name}: Compute factorial({i})...")
await asyncio.sleep(1)
f *= i
print(f"Task {name}: factorial({number}) = {f}")
async def main():
# Schedule three calls *concurrently*:
await asyncio.gather(
factorial("A", 2),
factorial("B", 3),
factorial("C", 4),
)
asyncio.run(main())
# Expected output:
#
# Task A: Compute factorial(2)...
# Task B: Compute factorial(2)...
# Task C: Compute factorial(2)...
# Task A: factorial(2) = 2
# Task B: Compute factorial(3)...
# Task C: Compute factorial(3)...
# Task B: factorial(3) = 6
# Task C: Compute factorial(4)...
# Task C: factorial(4) = 24
如果所有的awaitable
物件都執行完畢,則返回 awaitable物件執行結果的聚合列表。返回值的順序於aws
引數的順序一致。
簡單修改以上程式碼:
import asyncio
async def factorial(name, number):
f = 1
for i in range(2, number + 1):
#print(f"Task {name}: Compute factorial({i})...")
await asyncio.sleep(1)
f *= i
#print(f"Task {name}: factorial({number}) = {f}")
return number
async def main():
# Schedule three calls *concurrently*:
print(await asyncio.gather(
factorial("A", 2),
factorial("B", 3),
factorial("C", 4),
))
asyncio.run(main())
# Expected output:
#
#[2, 3, 4]#await asyncio.gather()的返回值是一個列表,
#分別對應factorial("A", 2),factorial("B", 3),factorial("C", 4)的執行結果。
如果return_execptions
引數為False
(預設值即為False
),引發的第一個異常會立即傳播給等待gather()
的任務,即呼叫await asyncio.gather()
物件。序列中其他awaitable
物件的執行不會受影響。例如:
import asyncio
async def division(divisor, dividend):
if divisor == 0:
raise ZeroDivisionError
else:
print(f"{dividend}/{divisor}={dividend/divisor}")
return dividend/divisor
async def main():
# Schedule three calls *concurrently*:
print(await asyncio.gather(
division(0, 2),
division(1, 2),
division(2, 2),
))
asyncio.run(main())
執行結果:
2/1=2.0
2/2=1.0
Traceback (most recent call last):
File "test.py", line 19, in <module>
asyncio.run(main())
File "c:\Program Files\Python37\lib\asyncio\runners.py", line 43, in run
return loop.run_until_complete(main)
File "c:\Program Files\Python37\lib\asyncio\base_events.py", line 573, in run_until_complete
return future.result()
File "test.py", line 16, in main
division(2, 2),
File "test.py", line 6, in division
raise ZeroDivisionError
ZeroDivisionError
如果return_exceptions
引數為True
,異常會和正常結果一樣,被聚合到結果列表中返回。
對以上程式碼稍作修改,將return_exceptions
設為True
:
import asyncio
async def division(divisor, dividend):
if divisor == 0:
raise ZeroDivisionError
else:
print(f"{dividend}/{divisor}={dividend/divisor}")
return dividend/divisor
async def main():
# Schedule three calls *concurrently*:
print(await asyncio.gather(
division(0, 2),
division(1, 2),
division(2, 2),
return_exceptions=True
))
asyncio.run(main())
執行結果如下:
2/1=2.0
2/2=1.0
[ZeroDivisionError(), 2.0, 1.0]#錯誤不會向上傳播,而是作為結果返回
如果gather()
被取消,則提交的所有awaitable
物件(尚未執行完成的)都會被取消。例如:
import asyncio
async def division(divisor, dividend):
if divisor == 0:
raise ZeroDivisionError
else:
await asyncio.sleep(divisor)
print(f"{dividend}/{divisor}={dividend/divisor}")
return dividend/divisor
async def main():
# Schedule three calls *concurrently*:
t = asyncio.gather(
division(0, 2),
division(1, 5),
division(3, 6),
return_exceptions=True
)
await asyncio.sleep(2)
t.cancel()
await t
asyncio.run(main())
執行結果:
5/1=5.0 #除已執行的之外,其他的任務全部被取消
Traceback (most recent call last):
File "test.py", line 23, in <module>
asyncio.run(main())
File "c:\Program Files\Python37\lib\asyncio\runners.py", line 43, in run
return loop.run_until_complete(main)
File "c:\Program Files\Python37\lib\asyncio\base_events.py", line 573, in run_until_complete
return future.result()
concurrent.futures._base.CancelledError
#在return_exceptions=True的情況下,異常依然向上傳播。
如果aws
中某些Task
或Future
被取消,gather()
呼叫不會被取消,被取消的Task
或Future
會以引發CancelledError
的方式被處理。這樣可以避免個別awaitable
物件的取消操作影響其他awaitable
物件的執行。
例如:
import asyncio
async def division(divisor, dividend):
if divisor == 0:
raise ZeroDivisionError
else:
await asyncio.sleep(divisor)
print(f"{dividend}/{divisor}={dividend/divisor}")
return dividend/divisor
async def main():
# Schedule three calls *concurrently*:
task1 = asyncio.create_task(division(0, 2))
task2 = asyncio.create_task(division(1, 5))
task3 = asyncio.create_task(division(3, 6))
t = asyncio.gather(
task1,
task2,
task3,
return_exceptions=True
)
task1.cancel()
print(await t)
asyncio.run(main())
預期執行結果如下:
5/1=5.0
6/3=2.0
[CancelledError(), 5.0, 2.0] # 僅task1被取消,其他任務不受影響。
避免取消
awaitable asyncio.shield(aw, * , loop=None)
防止awaitable
物件被取消(cancelled)執行。
如果aw
引數是一個協程(coroutines)
,該物件會被自動封裝為Task
物件進行處理。
通常,程式碼:
#code 1
res = await shield(something())
同程式碼:
#code 2
res = await something()
是等價的。
特殊情況是,如果包含以上程式碼的協程
被 取消,code 1
與code 2
的執行效果就完全不同了:
code 1
中,運行於something()
中的任務 不會被取消。code 2
中,運行於something()
中的任務 會被取消。
在code 1
中,從something()
的視角看,取消操作並沒有發生。然而,事實上它的呼叫者確實被取消了,所以await shield(something())
仍然會引發一個CancelledError
異常。
import asyncio
import time
async def division(divisor, dividend):
if divisor == 0:
raise ZeroDivisionError
else:
await asyncio.sleep(divisor)
print(f"{time.strftime('%X')}:{dividend}/{divisor}={dividend/divisor}")
return dividend/divisor
async def main():
# Schedule three calls *concurrently*:
print(f"Start time:{time.strftime('%X')}")
task1 = asyncio.shield(division(1, 2))
task2 = asyncio.create_task(division(1, 5))
task3 = asyncio.create_task(division(3, 6))
res = asyncio.gather(task1, task2, task3, return_exceptions=True)
task1.cancel()
task2.cancel()
print(await res)
asyncio.run(main())
執行結果:
Start time:10:38:48
10:38:49:2/1=2.0
10:38:51:6/3=2.0
[CancelledError(), CancelledError(), 2.0]
#task1雖然被取消,但是division(1,2)依然正常執行了。
#task2被取消後,division(1,5)沒有執行
#雖然task1內的協程被執行,但返回值依然為CancelledError
如果something()
以其他的方式被取消,比如從自身內部取消,那麼shield()
也會被取消。
如果希望完全忽略取消操作
(不推薦這麼做),則可以將shield()
與try/except
結合起來使用:
try:
res = await shield(something())
except CancelledError:
res = None
超時(Timeouts)
coroutine asyncio.wait_for(aw,timeout,*,loop=None)
在timeout
時間之內,等待aw
引數指定的awaitable
物件執行完畢。
如果aw
是一個協程,則會被自動作為Task
處理。
timeout
可以是None
也可以是一個float
或int
型別的數字,表示需要等待的秒數。如果timeout
是None
,則永不超時,一直阻塞到aw
執行完畢。
如果達到timeout
時間,將會取消待執行的任務,引發asyncio.TimeoutError
.
如果想避免任務被取消,可以將其封裝在shield()
中。
程式會等待到任務確實被取消掉,所以等待的總時間會比timeout
略大。
如果await_for()
被取消,aw
也會被取消。
loop
引數將在Python3.10中刪除,所以不推薦使用。
示例:
async def eternity():
# Sleep for one hour
await asyncio.sleep(3600)
print('yay!')
async def main():
# Wait for at most 1 second
try:
await asyncio.wait_for(eternity(), timeout=1.0)
except asyncio.TimeoutError:
print('timeout!')
asyncio.run(main())
# Expected output:
#
# timeout!
Python3.7新特性:當aw
因為超時被取消,wait_for()
等到aw
確實被取消之後返回異常。在以前的版本中,wait_for
會立即返回異常。
等待原語(Waiting Primitives)
wait()
coroutine asyncio.wait(aws,*,loop=None,timeout=None,return_when=ALL_COMPLETED)
併發執行aws
中的awaitable
物件,一直阻塞到return_when
指定的情況出現。
如果aws
中的某些物件是協程(coroutine)
,則自動轉換為Task
物件進行處理。直接將coroutine
物件傳遞給wait()
會導致令人迷惑的執行結果,所以不建議這麼做。
返回值是兩個Task/Future
集合:(done,pending
)。
用法示例:
done,pending = await asyncio.wait(aws)
loop
引數將在Python3.10中刪除,所以不建議使用。
timeout
引數可以是一個int
或float
型別的值,可以控制最大等待時間。
需要注意的是,wait()
不會引發asyncio.TimeoutError
錯誤。返回前沒有被執行的Future
和Task
會被簡單的放入pending
集合。
return_when
決定函式返回的時機。它只能被設定為以下常量:
|Constant|Description|
|-|-|
|FIRST_COMPLETED|The function will return when any future finishes or is cancelled.|
|FIRST_EXCEPTION|The function will return when any future finishes by raising an exception. If no future raises an exception then it is equivalent to ALL_COMPLETED.|
|ALL_COMPLETED|The function will return when all futures finish or are cancelled.|
與wait_for()
不同,wait()
不會再超時的時候取消任務。
注意:
因為wait()
會自動將協程
轉換為Task物件
進行處理,然後返回這些隱式建立的Task
到(done,pending)集合,所以以下程式碼不會如預期的那樣執行。
async def foo():
return 42
coro = foo()
done, pending = await asyncio.wait({coro})
if coro in done:
# 因為wait()會自動將協程轉換為Task物件進行處理,然後返回這些隱式建立的Task到(done,pending)集合,所以這個條件分支永遠不會被執行。
上面的程式碼可以做如下修正:
async def foo():
return 42
task = asyncio.create_task(foo())
done, pending = await asyncio.wait({task})
if task in done:
# 這回可以正常執行了.
所以,正如上文所講,不建議將coroutine
物件直接傳遞給wait()
。
as_completed()
asyncio.as_completed(aws,*,loop=None,timeout=None)
併發執行aws
中的awaitable
物件。返回一個Future
物件迭代器。每次迭代時返回的Future
物件代表待執行的awaitable
物件集合裡最早出現的結果。注意:迭代器返回的順序與aws
列表的順序無關,只與結果出現的早晚有關。
如果超時之前還有Future
物件未完成,則引發asyncio.TimeoutError
異常。
用法示例:
for f in as_completed(aws):
earliest_result = await f
# ...
以下為一個完整的例子:
import asyncio
import time
async def eternity(delay):
await asyncio.sleep(delay)
print(f"delay for {delay} seconds.")
return delay
async def main():
print(f"Start at: {time.strftime('%X')}")
tasks = [eternity(i) for i in range(10)]
for f in asyncio.as_completed(tasks):
res = await f
print(f"End at: {time.strftime('%X')}")
asyncio.run(main())
執行結果如下:
Start at: 17:19:11
delay for 0 seconds.
delay for 1 seconds.
delay for 2 seconds.
delay for 3 seconds.
delay for 4 seconds.
delay for 5 seconds.
delay for 6 seconds.
delay for 7 seconds.
delay for 8 seconds.
delay for 9 seconds.
End at: 17:19:20
從其他執行緒排程執行(Scheduling From Other Threads)
asyncio.run_coroutine_threadsafe(coro,loop)
向loop
指定的事件迴圈提交一個由coro
指定協程。執行緒安全。
返回一個concurrent.futures.Future
物件,等待另一個執行緒返回結果。
這個函式用於從當前執行緒
向執行事件迴圈的執行緒
提交協程(coroutine)
。
例如:
# Create a coroutine
coro = asyncio.sleep(1, result=3)
# Submit the coroutine to a given loop
future = asyncio.run_coroutine_threadsafe(coro, loop)
# Wait for the result with an optional timeout argument
assert future.result(timeout) == 3
如果協程
出現異常,返回的Future
會收到通知。返回的Future
也可以被用作取消事件迴圈中的任務:
try:
result = future.result(timeout)
except asyncio.TimeoutError:
print('The coroutine took too long, cancelling the task...')
future.cancel()
except Exception as exc:
print(f'The coroutine raised an exception: {exc!r}')
else:
print(f'The coroutine returned: {result!r}')
可以參考併發與多執行緒章節。
與其他asyncio函式不同,該函式需要 顯式 傳遞loop
引數。
新增於Python 3.5.1
自查(Introspection)
current_task()
asyncio.current_task(loop=None)
返回事件迴圈中正在執行的Task
例項,如果沒有Task
在執行,則返回None
。
如果loop
為None
,則使用get_running_loop()
獲取當前事件迴圈。
新增於Python3.7
all_tasks()
asyncio.all_tasks(loop=None)
返回事件迴圈中尚未執行結束的Task
物件集合。
如果loop
為None
,則,使用get_running_loop()
獲取當前事件迴圈。
新增於Python3.7
Task物件
class asyncio.Task(coro,*,loop=None)
類似與Future
物件,用於執行Python協程。非執行緒安全。
Tasks
用於在事件迴圈
中執行協程
。如果協程
等待一個Future
,那麼Task
會暫停協程
的執行,直到Future
執行完成。當Future
完成時,協程
的執行會恢復。
事件迴圈的 協作排程 模式:一個事件迴圈同一時間只執行一個Task
。當這個Task
等待某個Future
返回時,事件迴圈執行其他的Task
、回撥
或IO操作
。
可以通過高層函式asyncio.create_task()
建立Task
,或者通過底層函式loop.create_task()
和ensure_future()
建立Task
。但是不建議直接例項化Task
物件。
如果想要取消一個Task
的執行,可以使用cancel()
方法。呼叫cancel()
會引起Task
物件向被封裝的協程
丟擲CancelledError
異常。當取消行為發生時,如果協程
正在等待某個Future
物件執行,該Future
物件將被取消。
cancelled()
方法用於檢查某個Task
是否已被取消。如果Task
封裝的協程
沒有阻止CancelledError
異常,且Task
確實被取消了,則該方法返回True
。
asyncio.Task
繼承了Future
類中除Future.set_result()
和Future.set_exception()
以外的所有方法。
Task
物件支援contextvars
模組:當一個Task
被建立的時候,它會複製當前的上下文,然後在複製的上下文副本中執行協程。
Python3.7中的變更:添加了對contextvars
模組的支援。
cancel()
申請取消任務。
將在下一個事件迴圈週期中將CancelledError
異常拋給封裝在Task
中的協程。
收到CancelledError
異常後,協程
有機會處理異常,甚至以try ...except CancelledError ...finally
來拒絕請求。因此,與Future.cancel()
不同,Task.cancel()
不能保證Task
一定被取消掉。當然,拒絕取消請求這種操作並不常見,而且很不提倡。
以下例子可以說明協程如何攔擷取消請求:
import asyncio
async def cancel_me():
print('cancel_me(): before sleep')
try:
# Wait for 1 hour
await asyncio.sleep(3600)
except asyncio.CancelledError:
print('cancel_me(): cancel sleep')
raise
finally:
print('cancel_me(): after sleep')
async def main():
# Create a "cancel_me" Task
task = asyncio.create_task(cancel_me())
# Wait for 1 second
await asyncio.sleep(1)
task.cancel()
try:
await task
except asyncio.CancelledError:
print("main(): cancel_me is cancelled now")
asyncio.run(main())
# Expected output:
#
# cancel_me(): before sleep
# cancel_me(): cancel sleep
# cancel_me(): after sleep
# main(): cancel_me is cancelled now
cancelled()
如果Task
已經被取消,則返回True
。
當取消請求通過cancel()
被提交,且Task
封裝的協程
傳播了拋給它的CancelledError
異常,則此Task
被取消。
done()
如果Task
已完成,則返回True
。
Task
完成有三種情況:
- 封裝的協程已返回
- 封裝的協程已丟擲異常
Task
被取消
result()
返回Task
的執行結果。
如果Task
已經完成,則返回Task
封裝的協程的執行結果(如果Task
封裝的協程引發異常,則重新引發該異常)。
如果Task
已經取消,則該方法引發CancelledError
異常。
如果Task
的結果還不可用,該方法引發InvalidStateError
異常。
exception()
返回Task
的異常。
如果封裝的協程引發了異常,則返回此異常。如果封裝的協程執行正常,則返回None
。
如果Task
已被取消,則引發CancelledError
異常。
如果Task
尚未完成,則引發InvalidStateError
異常。
add_done_callback()
新增一個回撥函式,在Task
完成後執行。
這個方法只應用在基於回撥的底層程式設計中。
具體細節可以參考Future.remove_done_callback()
get_stack(* ,limit=None)
返回此Task
的堆疊幀列表。
- 如果封裝的協程未完成,此方法返回它暫停位置的堆疊。
- 如果封裝的協程已經完成或已被取消,此方法返回一個空的列表。
- 如果封裝的協程因異常而結束,此方法返回異常回溯列表。
幀的順序總是 由舊到新。
暫停中的協程只返回一個堆疊幀。
可選引數limit
用於限定返回幀的最大數目。預設情況下,所有有效的幀都會返回。
在返回堆疊和返回異常回溯時,列表的順序會有所不同:
- 最新的堆疊幀會被返回
- 最老的回溯幀會被返回(這和異常回溯模組的機制有關)
print_stack(* ,limit=None,file=None)
列印Task
的棧幀或異常回溯。
此方法用於輸出由get_stack()
取回的幀列表,輸出形式類似於回溯(traceback)模組
limit
引數會直接傳遞給get_stack()
。
file
引數指定輸出的I/O流,預設為sys.stderr
。
classmethod all_tasks(loop=None)
返回一個事件迴圈上所有任務的集合。
預設情況下,當前事件迴圈上所有的任務都會被返回。如果loop
引數為'None',則通過get_event_loop()
方法獲取當前事件迴圈。
此方法將在Python3.9中被移除,所以不建議使用。可以使用asyncio.all_tasks()
代替。
calssmethod current_task(loop=None)
返回當前正在執行的Task
或None
。
如果loop
引數為'None',則通過get_event_loop()
方法獲取當前事件迴圈。
此方法將在Python3.9中被移除,所以不建議使用。可以使用asyncio.current_task()
代替。
基於生成器的協程(Generator-based Coroutines)
提示:對基於生成器的協程的支援將在Python3.10中移除,不建議使用。
基於生成器的協程是早期的非同步實現方式,出現在async/await
語法之前,使用yield from
表示式等待Future
或其他協程。
基於生成器的協程應該用@asyncio.coroutine
來修飾,儘管這不是強制的。
@asyncio.coroutine
基於生成器的協程的修飾器。
這個修飾器能使傳統的基於生成器的協程
與async/await
語法相容:
@asyncio.coroutine
def old_style_coroutine():
yield from asyncio.sleep(1)
async def main():
await old_style_coroutine()
此修飾器將在Python3.10中被移除,所以不建議再使用。
此修飾器不能用於async def
的協程中。
asyncio.iscoroutine(obj)
如果obj
物件是一個coroutine
物件,則返回True
。
此方法與inspect.iscoroutine()
不同,因為它對基於生成器的協程也返回True
。
asyncio.iscoroutinefunction(func)
如果func
是一個coroutine
方法,則返回True
。
此方法inspect.iscoroutinefunction()
不同,因為它對用@coroutine
修飾的基於生成器的協程也返回True
。