8.3. イベントループ

イベントループは asyncio がその下で実行するエンジンです。プログラム内のすべてのタスクのリストを保持し、各タスクにその次の await まで実行するよう求め、次の実行可能なタスクに移ります。実行可能なタスクがないときは待機します — その実際の待機こそが、ファームウェアが他のことを実行したり、省電力スリープが作動したりするために CPU を利用可能にするものです — タスクが await していた何かが利用可能になるまで待機し、その後そのタスクを再開します。これを永遠に繰り返します。

ほとんどのアプリケーションはループと直接やり取りすることはありません。ループは asyncio.run() を呼び出した結果です。アプリケーションはコルーチンを記述し、それらをタスクとしてスケジュールし、残りはループが行います。

8.3.1. asyncio.run() が実際に行うこと

単一の呼び出し:

asyncio.run(main())

は、ループがアプリケーションに代わって管理する、より長い一連の処理の省略形です:

  1. イベントループがまだ存在しない場合は作成します。

  2. 提供されたコルーチンをタスクでラップし、ループのトップレベルのエントリポイントとしてスケジュールします。

  3. ループを実行します — 実行可能なタスクをステップ実行し、実行可能なものがないときは待機し、await が完了するとタスクを再開します — トップレベルのタスクが返るか送出するまで。

  4. アプリケーションが作成した、まだ実行中のタスクをすべてキャンセルします。

  5. トップレベルのコルーチンが返したもの(または送出したものを再送出して)を返します。

8.3.2. プログラムごとに 1 つのループ

MicroPython の asyncio には 1 つの イベントループしかありません、それだけです。新しいループを作成することはなく、あるループを別のループの内側にネストすることもありません。すでにループ上で実行中のコルーチンの内部から asyncio.run() を呼び出すことはエラーです。ループはすでにそこにあり、コルーチンは開始したかったものを await するだけでよいのです。

実際には、ルールは前のページの締めくくりの行と同じです:プログラムごとに asyncio.run() 呼び出しはちょうど 1 つ、最上部にあり、その背後には単一の async def main() があります。それ以外のすべては main の内部に存在します。

8.3.3. ループへの直接アクセス

アプリケーションがループそのものに触れる必要がある稀なケース — ほとんどは診断と例外ハンドラ — のために、asyncio.get_event_loop()Loop オブジェクトを返します。そこからアプリケーションは、カスタム例外ハンドラをインストールしたり、ループが何をしているかを検査したり、(ごくたまに)asyncio.create_task() の代わりに create_task() を直接呼び出したり(それらは同じ操作です)できます。

Loop が公開するメソッドの全セット — run_forever()stop()set_exception_handler() など — は、このセクションの後半の ループ制御 のページで扱います。それまでは、asyncio.run(main()) がアプリケーションに必要なすべてです。