Pythonでasyncioを使用する方法

Pythonの非同期プログラミング機能(略してasync)を使用すると、独立したタスクが終了するのを待たずに、より多くの作業を実行するプログラムを作成できます。asyncioPythonのに含まれているライブラリは、他のすべての待機をすることなく、ディスクまたはネットワークI / Oを処理するために使用非同期にツールがあります。

asyncio 非同期操作を処理するための2種類のAPIを提供します: 高レベル と 低レベル。高レベルのAPIは最も一般的に有用であり、さまざまなアプリケーションに適用できます。低レベルのAPIは強力ですが、複雑であり、使用頻度も低くなります。

この記事では、高レベルのAPIに焦点を当てます。以下のセクションでは、で最も一般的に使用される高レベルAPIasyncioについて説明し 、非同期タスクを含む一般的な操作にそれらを使用する方法を示します。 

Pythonでの非同期にまったく慣れていない場合、またはその動作について復習を使用できる場合は、ここに飛び込む前に、Python非同期の概要を読んでください。

Pythonでコルーチンとタスクを実行する

当然、の最も一般的な使用法asyncioは、Pythonスクリプトの非同期部分を実行することです。これは、コルーチンとタスクの操作方法を学ぶことを意味します。 

コルーチンやタスクを含むPythonの非同期コンポーネントは、他の非同期コンポーネントでのみ使用でき、従来の同期Pythonでは使用できないasyncio ため、ギャップを埋める必要があります 。これを行うには、次のasyncio.run 関数を使用し ます。

非同期をインポート

async def main():

印刷(「5秒待っています。」)

range(5)の_の場合:

asyncio.sleep(1)を待つ

印刷( "。")

印刷(「待機が終了しました。」)

asyncio.run(main())

これは実行され main()、コルーチンが main() 起動し、結果が返されるのを待ちます。

原則.run() として、Pythonプログラムには1つのmain() 関数のみが必要であるのと同様に、Pythonプログラムには1つのステートメントのみが含ま れる必要があり ます。非同期を不注意に使用すると、プログラムの制御フローが読みにくくなる可能性があります。プログラムの非同期コードへの単一のエントリポイントを持つことで、問題が発生するのを防ぎます。

非同期関数は、タスク、またはコルーチンをラップしてそれらの実行を支援するオブジェクトとしてスケジュールすることもでき ます。

async def my_task():

do_something()

task = asyncio.create_task(my_task())

my_task() 次に、イベントループで実行され、その結果がに格納され taskます。

結果を取得するタスクが1つしかない場合は、を使用asyncio.wait_for(task) してタスクが終了するの を待ってから、を使用 task.result() して結果を取得できます。ただし、実行するタスクの数をスケジュールしていて、 それらすべてが完了するのを待ちたい場合は 、を使用 asyncio.wait([task1, task2]) して結果を収集します。(操作を特定の時間を超えて実行したくない場合は、操作のタイムアウトを設定できることに注意してください。)

Pythonで非同期イベントループを管理する

のもう1つの一般的な使用法 asyncio は、非同期イベントループを管理すること です。イベントループは、非同期関数とコールバックを実行するオブジェクトです。を使用すると自動的に作成されます asyncio.run()。通常、プログラムごとに1つの非同期イベントループのみを使用して、管理しやすくします。

サーバーなどのより高度なソフトウェアを作成している場合は、イベントループへの低レベルのアクセスが必要になります。そのために、「フードを持ち上げて」、イベントループの内部を直接操作することができます。しかし、単純な仕事の場合は、そうする必要はありません。

Pythonでストリームを使用してデータを読み書きします

非同期の最良のシナリオは、アプリケーションが他のリソースが結果を返すのを待つのをブロックする可能性がある、長時間実行されるネットワーク操作です。その asyncio ために、ネットワークI / Oを実行するための高レベルのメカニズムであるストリームを提供します。これには、ネットワーク要求のサーバーとして機能することも含まれます。

asyncio 2つのクラスとStreamReader を 使用 StreamWriterして、ネットワークから高レベルで読み取りと書き込みを行います。ネットワークから読み取りたい場合は、を使用 asyncio.open_connection() して接続を開きます。この関数はStreamReader と StreamWriter オブジェクトのタプルを返し、  それぞれで.read() と .write()メソッドを使用 して通信します。

リモートホストから接続を受信するには、を使用します asyncio.start_server()。このasyncio.start_server()関数client_connected_cbは、要求を受信するたびに呼び出されるコールバック関数を引数として取ります 。このコールバック関数は、インスタンス StreamReader とStreamWriter を引数として受け取るため、サーバーの読み取り/書き込みロジックを処理できます。(asyncio駆動型 aiohttp ライブラリを使用する単純なHTTPサーバーの例については、ここを参照してください 。)

Pythonでタスクを同期する

非同期タスクは単独で実行される傾向がありますが、相互に通信したい場合があります。 asyncio タスク間で同期するためのキューおよびその他のいくつかのメカニズムを提供します。

  • キュー: asyncio キューを使用すると、非同期関数でPythonオブジェクトを並べて、他の非同期関数で使用できます。たとえば、動作に基づいてさまざまな種類の関数間でワークロードを分散できます。
  • 同期プリミティブ:ロック、イベント、条件、およびセマフォはasyncio、従来のPythonの対応物と同様に機能します。 

これらすべてのメソッドについて覚えておくべきことの1つは、 スレッドセーフではないということです 。これは、同じイベントループで実行されている非同期タスクの問題ではありません。ただし、別のイベントループ、OSスレッド、またはプロセス内のタスクと情報を共有しようとしている場合は、threading モジュールとそのオブジェクトを使用して共有する必要があります 。

さらに、  スレッドの境界を越えてコルーチンを起動する場合は、 asyncio.run_coroutine_threadsafe() 関数を使用し、イベントループを渡してパラメーターとして使用します。

Pythonでコルーチンを一時停止する

のもう1つの一般的な使用法asyncio、および十分に議論されていない使用法は 、コルーチン内で任意の長さの時間を待つことです。time.sleep() これには使用できません。使用しないと 、プログラム全体がブロックされます。代わりに、を使用してください asyncio.sleep()。これにより、他のコルーチンが実行を継続できます。

Pythonで低レベルの非同期を使用する

最後に、作成しているアプリにasyncio低レベルのコンポーネントが必要だと思われる場合は、コーディングを開始する前に確認してください。必要な処理を実行する非同期のPythonライブラリが誰かによってすでに作成されている可能性があります。

たとえば、非同期DNSクエリが必要な場合は、aiodns ライブラリを確認してください asyncSSH。非同期SSHセッションの場合は、があり ます。キーワード「async」(およびその他のタスク関連キーワード)でPyPIを検索するか、手作業で作成されたAwesomeAsyncioリストでアイデアを確認してください。