これらの3つのメカニズムすべてが存在する理由は、それらが異なる長所と短所を持っているためです。
まず、多数の小さな独立したタスクがあり、それらをバッチ処理する賢明な方法がない場合(通常、これはC10kサーバーを作成していることを意味しますが、それが唯一の可能なケースではありません)、マイクロスレッドが勝ちます。すべてが停止するか、単に失敗する前に、数百のOSスレッドまたはプロセスしか実行できません。したがって、マイクロスレッドを使用するか、自動同時実行をあきらめて、明示的なコールバックまたはコルーチンの作成を開始します。これは、マイクロスレッドが勝つ唯一の時間です。それ以外の点では、いくつかの点が正しく機能しないことを除けば、OSスレッドと同じです。
次に、コードがCPUにバインドされている場合は、プロセスが必要です。マイクロスレッドは本質的にシングルコアソリューションです。Pythonのスレッドは、GILが原因で、通常、うまく並列化できません。プロセスは、OSが処理できる限り多くの並列処理を取得します。したがって、プロセスにより、4コアシステムでコードを4倍の速度で実行できます。他には何もしません。(実際、さらに進んで別々のコンピューターに分散したい場合もありますが、それについては尋ねませんでした。)ただし、コードがI / Oバウンドの場合、コア並列処理は役に立たないため、スレッドは同じです。プロセスとして良い。
共有された可変データがたくさんある場合、事態は困難になります。multiprocessing.Array
プロセスでは、の代わりに使用するなど、すべてを共有可能な構造に明示的に配置する必要がありますがlist
、これは悪夢のように複雑になります。スレッドはすべてを自動的に共有します。つまり、どこにでも競合状態があります。つまり、フローを慎重に検討し、ロックを効果的に使用する必要があります。プロセスを使用すると、経験豊富な開発者は、すべてのテストデータで機能するシステムを構築できますが、新しい入力セットを提供するたびに再編成する必要があります。スレッドを使用すると、経験豊富な開発者は、数週間実行されるコードを記述してから、誤ってサイレントに全員のクレジットカード番号をスクランブリングすることができます。
問題をよりよく理解しているので、これら2つのどちらがあなたをもっと怖がらせますか。どちらかを実行してください。または、可能であれば、一歩下がってコードを再設計し、共有データのほとんどを独立または不変にするようにしてください。これは(物事を遅すぎたり、理解しづらくしたりせずに)不可能かもしれませんが、それを決定する前にそれについてよく考えてください。
独立したデータや共有された不変のデータがたくさんある場合は、スレッドが明らかに勝ちます。プロセスには、明示的な共有(multiprocessing.Array
再度のように)またはマーシャリングのいずれかが必要です。multiprocessing
また、サードパーティの代替手段により、すべてが選択可能であるという単純なケースではマーシャリングが非常に簡単になりますが、値を直接渡すほど単純ではなく、速度も大幅に低下します。
残念ながら、渡す必要のある不変のデータがたくさんあるほとんどの場合は、CPUの並列処理が必要な場合とまったく同じです。つまり、トレードオフがあります。そして、このトレードオフに対する最善の答えは、現在の4コアシステムのOSスレッドかもしれませんが、2年以内に使用している16コアシステムのプロセスです。(たとえば、multiprocessing.ThreadPool
またはなどを整理し、後で、またはランタイム構成スイッチを使用しconcurrent.futures.ThreadPoolExecutor
て簡単に切り替えると、問題はほぼ解決します。ただし、これが常に可能であるとは限りません。)Pool
ProcessPoolExecutor
最後に、アプリケーションが本質的にイベントループを必要とする場合(GUIアプリやネットワークサーバーなど)、最初に好きなフレームワークを選択します。たとえば、PySide
vs wx
。、またはtwisted
vs.gevent
を使用したコーディングは、マイクロスレッドとOSスレッドを使用したコーディングよりも大きな違いがあります。そして、フレームワークを選択したら、実際の同時実行性が必要だと思ったイベントループをどれだけ活用できるかを確認してください。たとえば、30秒ごとに実行するコードが必要な場合は、そのためのスレッド(microまたはOS)を開始せず、フレームワークに必要に応じてスケジュールするように依頼します。