Wait

  • awaitableオブジェクト(Taskやコルーチン)はさまざまな条件で待機できる

  • タイムアウトを設定できる

  • 例外に応じて処理を制御できる

タイムアウト
要素の終了待機

Future

  • 非同期処理の最終結果を表現する特別な低レベルのawaitableオブジェクト

    • pending

    • finished

    • cancelled

  • コルーチンはFutureの結果が返されるか例外が送出されるまでawaitされる

asyncio.wait_for

  • awaitableオブジェクトが完了するかタイムアウトになるのを待つ

  • awaitableオブジェクトがコルーチンだった場合はTaskとしてスケジュールされる

  • タイムアウトの場合はTaskをキャンセルし、 asyncio.TimeoutError を送出する

 1import asyncio
 2import time
 3
 4
 5async def main():
 6    await asyncio.wait_for(asyncio.sleep(3), timeout=1)
 7
 8
 9start = time.time()
10asyncio.run(main())
11print(f"time: {time.time() - start}")

asyncio.TimeoutError になる

asyncio.as_completed

  • awaitableオブジェクトを同時に実行する

  • 完了した順に、イテレータを返す

  • イテレーションが完了する前にタイムアウトした場合は asyncio.TimeoutError を送出する

 1import asyncio
 2import time
 3
 4
 5async def neru(n):
 6    await asyncio.sleep(n)
 7    return n
 8
 9
10async def main():
11    for f in asyncio.as_completed([neru(2), neru(4), neru(1)], timeout=3):
12        result = await f
13        print(f"{result=}")
14
15
16start = time.time()
17asyncio.run(main())
18print(f"time: {time.time() - start}")
result=1
result=2
Traceback (most recent call last):
  File "/home/driller/repo/events/20210912/docs/source/code/as_completed.py", line 17, in <module>
    asyncio.run(main())
  File "/usr/local/lib/python3.9/asyncio/runners.py", line 44, in run
    return loop.run_until_complete(main)
  File "/usr/local/lib/python3.9/asyncio/base_events.py", line 642, in run_until_complete
    return future.result()
  File "/home/driller/repo/events/20210912/docs/source/code/as_completed.py", line 12, in main
    result = await f
  File "/usr/local/lib/python3.9/asyncio/tasks.py", line 613, in _wait_for_one
    raise exceptions.TimeoutError
asyncio.exceptions.TimeoutError

asyncio.wait

  • awaitableオブジェクトを同時に実行する

  • 完了したタスクと保留中のタスクのset(集合)を返す

  • return_when でいつ結果を返すかを指定する

 1import asyncio
 2import time
 3
 4
 5async def main():
 6    tasks = [
 7        asyncio.create_task(asyncio.sleep(1)),
 8        asyncio.create_task(asyncio.sleep(3)),
 9    ]
10    done, pending = await asyncio.wait(tasks, timeout=2)
11    
12    for t in tasks:
13        print(f"{t} in done: {t in done}")
14        print(f"{t} in pending: {t in pending}")
15
16
17start = time.time()
18asyncio.run(main())
19print(f"time: {time.time() - start}")
<Task finished name='Task-2' coro=<sleep() done, defined at /usr/local/lib/python3.9/asyncio/tasks.py:636> result=None> in done: True
<Task finished name='Task-2' coro=<sleep() done, defined at /usr/local/lib/python3.9/asyncio/tasks.py:636> result=None> in pending: False
<Task pending name='Task-3' coro=<sleep() running at /usr/local/lib/python3.9/asyncio/tasks.py:654> wait_for=<Future pending cb=[<TaskWakeupMethWrapper object at 0x7f0123edb2b0>()]>> in done: False
<Task pending name='Task-3' coro=<sleep() running at /usr/local/lib/python3.9/asyncio/tasks.py:654> wait_for=<Future pending cb=[<TaskWakeupMethWrapper object at 0x7f0123edb2b0>()]>> in pending: True
time: 2.0029919147491455

return_when

asyncio.waitがいつ返すかを指定

FIRST_COMPLETED
  • いずれかのFutureが終了したかキャンセルされたときに返す

FIRST_EXCEPTION
  • いずれかのFutureが例外の送出で終了した場合に返す

  • 例外を送出したFutureがない場合は、 ALL_COMPLETED と等価になる

ALL_COMPLETED
  • すべてのFutureが終了したかキャンセルされたときに返す(デフォルト)

 1import asyncio
 2import time
 3from asyncio.tasks import FIRST_COMPLETED
 4
 5
 6async def main():
 7    tasks = [
 8        asyncio.create_task(asyncio.sleep(1)),
 9        asyncio.create_task(asyncio.sleep(3)),
10    ]
11    done, pending = await asyncio.wait(tasks, timeout=2, return_when=FIRST_COMPLETED)
12
13    for t in tasks:
14        print(f"{t} in done: {t in done}")
15        print(f"{t} in pending: {t in pending}")
16
17
18start = time.time()
19asyncio.run(main())
20print(f"time: {time.time() - start}")
<Task finished name='Task-2' coro=<sleep() done, defined at /usr/local/lib/python3.9/asyncio/tasks.py:636> result=None> in done: True
<Task finished name='Task-2' coro=<sleep() done, defined at /usr/local/lib/python3.9/asyncio/tasks.py:636> result=None> in pending: False
<Task pending name='Task-3' coro=<sleep() running at /usr/local/lib/python3.9/asyncio/tasks.py:654> wait_for=<Future pending cb=[<TaskWakeupMethWrapper object at 0x7fe15a347400>()]>> in done: False
<Task pending name='Task-3' coro=<sleep() running at /usr/local/lib/python3.9/asyncio/tasks.py:654> wait_for=<Future pending cb=[<TaskWakeupMethWrapper object at 0x7fe15a347400>()]>> in pending: True
time: 1.0022432804107666

練習問題

  1. asyncio.as_completed に複数のコルーチンを渡し、うち1つのコルーチンは例外を送出してください

  2. 1.の例外をtry-except処理してください