8.3. イベントループ¶
イベントループは asyncio がその下で実行するエンジンです。プログラム内のすべてのタスクのリストを保持し、各タスクにその次の await まで実行するよう求め、次の実行可能なタスクに移ります。実行可能なタスクがないときは待機します — その実際の待機こそが、ファームウェアが他のことを実行したり、省電力スリープが作動したりするために CPU を利用可能にするものです — タスクが await していた何かが利用可能になるまで待機し、その後そのタスクを再開します。これを永遠に繰り返します。
ほとんどのアプリケーションはループと直接やり取りすることはありません。ループは asyncio.run() を呼び出した結果です。アプリケーションはコルーチンを記述し、それらをタスクとしてスケジュールし、残りはループが行います。
8.3.1. asyncio.run() が実際に行うこと¶
単一の呼び出し:
asyncio.run(main())
は、ループがアプリケーションに代わって管理する、より長い一連の処理の省略形です:
イベントループがまだ存在しない場合は作成します。
提供されたコルーチンをタスクでラップし、ループのトップレベルのエントリポイントとしてスケジュールします。
ループを実行します — 実行可能なタスクをステップ実行し、実行可能なものがないときは待機し、await が完了するとタスクを再開します — トップレベルのタスクが返るか送出するまで。
アプリケーションが作成した、まだ実行中のタスクをすべてキャンセルします。
トップレベルのコルーチンが返したもの(または送出したものを再送出して)を返します。
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()) がアプリケーションに必要なすべてです。