8.16. まとめ

ここまでで asyncio の API、すなわちスクリプトが単一の CPU 上で複数のジョブを並行実行するために必要な部品を一通り見てきました:

  • 協調的スケジューリング -- モジュールの残りすべてが拠って立つモデルです。実行中のコルーチンは await するまでループを独占し、切り替えはそれらの await の時点でのみ発生します。

  • コルーチンとタスク -- async def は作業単位を定義します。asyncio.create_task() はその 1 つを並行してスケジュールし、アプリケーションが後で待機・キャンセル・識別できる Task を返します。

  • イベントループ -- コルーチンとタスクを実行するエンジンです。asyncio.run() はほとんどのスクリプトが必要とする唯一のエントリーポイントであり、Loop クラスは、それを必要とするまれなケースのために残りの機能を公開します。

  • 協調 -- 分散と集約のための gather()、期限のための wait_for()Task.cancelfinally 節によるクリーンアップパターン、タスクや gather 呼び出しを通じた例外の伝播、そしてループの例外ハンドラフックです。

  • 同期プリミティブ -- コルーチン間のシグナリングのための Event、await をまたいで共有リソースへのアクセスを直列化するための Lock、そして割り込みハンドラから asyncio タスクを起こすための ThreadSafeFlag です。

  • カスタム非同期オブジェクト -- アプリケーションのクラスを asyncio のイディオムに組み込めるようにする言語フックです。それ自体が await の対象となるオブジェクトのための __await__async for のための __aiter__ / __anext__、そして async with のための __aenter__ / __aexit__ です。

  • フレームキャプチャ -- csi.CSI.snapshot()await しやすいコルーチンに変えるラッパーであり、これによりキャプチャループが他の asyncio の作業と並行して動作します。

  • 落とし穴 -- await の付け忘れ、yield のないタイトループ、握りつぶされたキャンセル、await をまたいで変更される共有状態、そしてその他の asyncio 特有の罠です。

これだけ理解すれば、カメラの作業、ハードウェア I/O、そして並行するバックグラウンド作業を同じループ上で混在させるプログラムを書くのに十分です。

8.16.1. このリファレンスを後で使う

asyncio の各章はリファレンス資料として扱ってください。async with の形や、兄弟タスクの失敗時の gather() の正確な振る舞いを確認しに戻ってくる、というのが想定された使い方です。asyncio のリファレンスページは、「この呼び出しの正確な名前は何か」という疑問のために、すべての関数とクラスを 1 か所にまとめて列挙しています。

このモジュールの上に構築された、より豊富なプリミティブ(セマフォ、キュー、バリア、そしてアプリケーション向けのヘルパーの充実したコレクション)については、peterhinch/micropython-async リポジトリがコミュニティによって標準的に維持されているソースです。

8.16.2. ここから先へ

ネットワーキング が次の大きなトピックです。asyncio.open_connection()asyncio.start_server()、そして Stream クラスは、asyncio スクリプトがコルーチンの内側からネットワークの残りの部分と通信する方法であり、その下にある network モジュールと socket モジュールと組み合わせて使います。awaitTask、キャンセル、そして同期プリミティブについて学んだことはすべて、そのまま応用できます。