asyncio異步IO--協程(Coroutine)與任務(Task)詳解

摘要:本文翻譯自Coroutines and Tasks,主要介紹asyncio中用於處理協程和任務的方法和接口。在翻譯過程當中,譯者在官方文檔的基礎上增長了部分樣例代碼和示意圖表,以幫助讀者對文檔的理解。本文內容主要針對python3.7,在低版本的python中可能不適用,敬請留意。原創內容,如需轉載請註明出處。
譯者:馬鳴謙(郵箱:1612557569@qq.com)html

協程

協程(coroutines)是經過async/await定義函數或方法,是使用asyncio進行異步編程的首選途徑。以下,是一個協程的例子:python

import asyncio

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

上例中的 main 方法就是咱們定義的協程
咱們在交互環境(Python3.7)下執行以上代碼,看看效果:shell

>>> 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提供了三種執行協程的機制:安全

  1. 使用asyncio.run()執行協程。通常用於執行最頂層的入口函數,如main()
  2. 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秒bash

  1. 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秒less

「可等待」對象(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()
    )

底層函數返回Future對象的一個例子是:loop.run_in_executor

執行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中某些TaskFuture被取消,gather()調用不會被取消,被取消的TaskFuture會以引起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 1code 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也能夠是一個floatint類型的數字,表示須要等待的秒數。若是timeoutNone,則永不超時,一直阻塞到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參數能夠是一個intfloat類型的值,能夠控制最大等待時間。
須要注意的是,wait()不會引起asyncio.TimeoutError錯誤。返回前沒有被執行的FutureTask會被簡單的放入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
若是loopNone,則使用get_running_loop()獲取當前事件循環。
新增於Python3.7

all_tasks()

asyncio.all_tasks(loop=None)

返回事件循環中還沒有運行結束的Task對象集合。
若是loopNone,則,使用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用於限定返回幀的最大數目。默認狀況下,全部有效的幀都會返回。
在返回堆棧和返回異常回溯時,列表的順序會有所不一樣:

  • 最新的堆棧幀會被返回
  • 最老的回溯幀會被返回(這和異常回溯模塊的機制有關)

打印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)

返回當前正在運行的TaskNone
若是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

相關文章
相關標籤/搜索