web-dev-qa-db-ja.com

通常のスレッドでasyncioを使用する必要があるのはいつですか。その理由は何ですか。パフォーマンスが向上しますか?

Pythonのマルチスレッドについてはかなり基本的な理解があり、asyncioについてはさらに基本的な理解があります。

私は現在、メインスレッドでUIとユーザーIO)を処理する小さなCursesベースのプログラム(最終的には完全なGUIを使用する予定ですが、それは別の話です)を書いています。他の2つのデーモンスレッド(それぞれが独自のキュー/ワーカーメソッド-that-gets-things-from-a-queueを持つ):

  • 時間ベースの条件付き(メッセージボードへの投稿、受信メッセージなど)イベントの発生を監視し、必要なタスクを...に配置するwatcherスレッド。
  • もう一方の(worker)デーモンスレッドのキューは、それらを完了します。

3つのスレッドすべてが継続的に同時に実行されているため、いくつかの質問があります。

  • workerスレッドのキュー(または、より一般的には、任意のスレッドのキュー)が空の場合、何かが再び実行されるまで停止する必要がありますか、それとも継続的に実行したままにしても大丈夫ですか?並行スレッドは、キューを監視する以外に何もしていないときに、多くの処理能力を消費しますか?
  • 2つのスレッドのキューを組み合わせる必要がありますか? watcherスレッドは継続的に単一のメソッドを実行しているので、workerスレッドはwatcherスレッドが入れる単一のキューからタスクをプルできると思います。
  • 私はマルチプロセッシングではないので問題ないと思いますが、この設定はPythonのGIL(3.4にもまだ存在すると思います)の影響を受けますか?
  • watcherスレッドはそのように継続的に実行する必要がありますか?私が理解していることから、間違っている場合は訂正してください。asyncioは、イベントベースのマルチスレッドに使用されることになっています。これは、私がやろうとしていることに関連しているようです。
  • メインスレッドは基本的に、ユーザーがメニューの別の部分にアクセスするためにキーを押すのを常に待っています。これはらしい状況のようにasyncioは完璧ですが、繰り返しになりますが、よくわかりません。

ありがとう!

28
velocirabbit

ワーカースレッドのキュー(または、より一般的には、任意のスレッドのキュー)が空の場合、何かが再び実行されるまで停止する必要がありますか、それとも継続的に実行したままにしても大丈夫ですか?並行スレッドは、キューを監視する以外に何もしていないときに、多くの処理能力を消費しますか?

queue.get()へのブロッキング呼び出しを使用する必要があります。これにより、I/Oでスレッドがブロックされたままになります。つまり、GILが解放され、処理能力(または少なくともごくわずかな量)が使用されなくなります。 whileループで非ブロッキングgetを使用しないでください。これは、より多くのCPUウェイクアップが必要になるためです。

2つのスレッドのキューを組み合わせる必要がありますか?ウォッチャースレッドは継続的に単一のメソッドを実行しているため、ワーカースレッドは、ウォッチャースレッドが配置する単一のキューからタスクをプルするだけでよいと思います。

ウォッチャーが行っているのがキューから物事を引き出してすぐに別のキューに入れ、そこで1人のワーカーによって消費される場合は、不要なオーバーヘッドのように聞こえます。ワーカーで直接消費することもできます。それが事実であるかどうかは私には正確にはわかりませんが、-ウォッチャーがキューからを消費しているのですか、それとも単にアイテムを1つに入れているだけですか?それがisキューから消費している場合、誰がそれに何かを入れていますか?

私はマルチプロセッシングではないので問題ないと思いますが、この設定はPythonのGIL(3.4にもまだ存在すると思います)の影響を受けますか?

はい、これはGILの影響を受けます。一度に実行できるスレッドは1つだけPythonバイトコードであるため、スレッドがI/O(GILを解放する)を実行している場合を除いて、真の並列処理は取得されません。 CPUにバインドされたアクティビティを実行する場合は、可能であれば、multiprocessingを介して別のプロセスで実行することを真剣に検討する必要があります。

ウォッチャースレッドはそのように継続的に実行する必要がありますか?私が理解していることから、そして私が間違っている場合は訂正してください。asyncioはイベントベースのマルチスレッドに使用されることになっています。これは私がやろうとしていることに関連しているようです。

「継続的に走る」とはどういう意味か正確にはわからないので、わかりにくいです。それは継続的に何をしていますか?ほとんどの時間をqueueでスリープまたはブロックすることに費やしている場合は、問題ありません。どちらもGILを解放します。常に実際の作業を行っている場合は、GILが必要になるため、アプリ内の他のスレッドのパフォーマンスが低下します(同時に作業を行おうとしていると仮定します)。 asyncioは、I/Oバウンドのプログラム用に設計されているため、非同期I /を使用してsingleスレッドで実行できます。 O。 workerが何をしているかによっては、プログラムがそれに適しているようです。

メインスレッドは基本的に、ユーザーがメニューの別の部分にアクセスするためにキーを押すのを常に待っています。 これはasyncioが最適な状況のようですですが、繰り返しになりますが、よくわかりません。

ほとんどI/Oを待っているプログラムは、asyncioに適している可能性がありますが、curses(または最終的に選択する他のGUIライブラリ)をうまく機能させるライブラリが見つかった場合に限ります。 。ほとんどのGUIフレームワークには、asyncioと競合する独自のイベントループが付属しています。 GUIのイベントループをasyncioのイベントループとうまく連携させることができるライブラリを使用する必要があります。また、アプリケーションが使用する他の同期I/Oベースのライブラリ(データベースドライバなど)のasyncio互換バージョンが見つかることを確認する必要があります。

とはいえ、スレッドベースのプログラムからasyncioベースのプログラムに切り替えることで、パフォーマンスが向上することはほとんどありません。おそらくほぼ同じように動作します。 3つのスレッドしか扱っていないため、スレッド間のコンテキスト切り替えのオーバーヘッドはそれほど重要ではありません。したがって、シングルスレッドの非同期I/Oアプローチから切り替えても、それほど大きな違いはありません。 asyncioは、スレッド同期の複雑さを回避するのに役立ち(それがアプリの問題である場合、それが問題であるかどうかは明らかではありません)、少なくとも理論的には、アプリが潜在的に必要な場合はより適切にスケーリングしますたくさんのスレッドですが、そうではないようです。基本的には、どのスタイルでコーディングするかによって決まります(必要なすべてのasyncio互換ライブラリが見つかると仮定します)。

22
dano