web-dev-qa-db-ja.com

Pythonのスレッド化

Pythonでマルチスレッドアプリケーションを記述するために使用されるモジュールは何ですか?言語によって提供される基本的な並行性メカニズムと Stackless Python を知っていますが、それぞれの長所と短所は何ですか?

76
Jon

複雑さが増す順に:

スレッドモジュール を使用します

長所:

  • 独自のスレッドで任意の関数(実際には呼び出し可能な関数)を実行するのは本当に簡単です。
  • データの共有は簡単ではありませんが(ロックは決して簡単ではありません:)、少なくとも簡単です。

短所:

  • 前述のように by Juergen Pythonスレッドはインタープリターの状態に同時にアクセスすることはできません(1つの大きなロック、悪名高い Global Interpreter Lock 。)実際には、スレッドはI/Oバウンドタスク(ネットワーク、ディスクへの書き込みなど)には役立ちますが、同時計算にはまったく役立ちません。

multiprocessing モジュールを使用します

単純な使用例では、これはthreadingを使用するのとまったく同じように見えますが、各タスクは独自のスレッドではなく独自のプロセスで実行されます。 (ほぼ文字通り: Eliの例 を使用し、threadingmultiprocessingに、ThreadProcess、およびQueueに置き換えた場合(モジュール)multiprocessing.Queueで、正常に動作するはずです。)

長所:

  • すべてのタスクの実際の同時実行性(グローバルインタープリターロックなし)。
  • 複数のプロセッサに拡張でき、複数のmachinesにも拡張できます。

短所:

  • プロセスはスレッドよりも低速です。
  • プロセス間のデータ共有は、スレッドを使用する場合よりも注意が必要です。
  • メモリは暗黙的に共有されません。それを明示的に共有するか、変数をピクルして前後に送信する必要があります。これはより安全ですが、より困難です。 (それがますます重要になる場合、Python開発者はこの方向に人々を押しているようです。

Twisted などのイベントモデルを使用する

長所:

  • いつ、何を実行するかについて、優先順位を非常に細かく制御できます。

短所:

  • 優れたライブラリを使用しても、非同期プログラミングは通常、スレッドプログラミングよりも困難です。何が起こるかを理解するという点でも、実際に何が起こっているのかをデバッグするという点でも困難です。

allの場合、マルチタスクに関連する多くの問題、特にタスク間でデータを共有する方法のトリッキーな問題を既に理解していると思います。何らかの理由でロックと条件をいつどのように使用するかわからない場合は、それらから始めなければなりません。マルチタスクコードには微妙な点や落とし穴がたくさんあります。開始する前に概念を十分に理解しておくことが本当に最善です。

115
quark

「偽のスレッド」から外部フレームワークに至るまで、かなり多様な回答を得ていますが、Queue.Queue-CPythonスレッド化の「秘密のソース」。

拡張するには:純粋なPython CPUを大量に使用する処理をオーバーラップする必要がない限り(この場合はmultiprocessingが必要ですが、独自のQueue実装も必要です)だからあなたはいくつかの必要な注意を払って、私が与えている一般的なアドバイスを適用することができます;-)、Pythonの組み込みのthreadingは行います...しかし、それを使用すれば、はるかに良くなりますadvisedly、たとえば次のように。

スレッド化とマルチプロセッシングの主なプラスであると思われる共有メモリを「忘れる」-それはうまく機能せず、うまくスケーリングせず、決して持たず、決してしません。before一度セットアップされたデータ構造に対してのみ共有メモリを使用し、サブスレッドを生成し、その後変更されない-それ以外の場合は、singleそのリソースを担当し、Queueを介してそのスレッドと通信します。

ロックによって保護すると通常考えられるすべてのリソースに専用のスレッドを割り当てます:可変データ構造またはその凝集グループ、外部プロセス(DB、XMLRPCサーバーなど)、外部ファイルなどへの接続。その種の専用リソースを持たない、または必要としない汎用タスク用に小さなスレッドプールを取得します-do n't必要に応じてスレッドを生成します。または、スレッド切り替えのオーバーヘッドがあなたを圧倒します。

2つのスレッド間の通信は常にQueue.Queue-メッセージ処理の形式であり、マルチプロセッシングの唯一の健全な基盤です(トランザクションメモリ以外は有望ですが、In Haskell以外には生産に値する実装はありません)。

単一のリソース(またはリソースの小さな凝集セット)を管理する各専用スレッドは、特定のQueue.Queueインスタンスでリクエストをリッスンします。プール内のスレッドは、単一の共有Queue.Queueで待機します(キューは完全にスレッドセーフであり、wo n'tは失敗します)。

何らかのキュー(共有または専用)で要求をキューに入れるだけのスレッドは、結果を待たずにキューに入れて、先に進みます。最終的に要求または要求の結果または確認を必要とするスレッドは、作成したばかりのQueue.Queueのインスタンスとペア(要求、受信キュー)になり、最終的に、応答または確認が続行するために不可欠な場合、取得(待機)します)受信キューから。エラー応答と実際の応答または確認を取得する準備ができていることを確認してください(Twistedのdeferredsは、この種の構造化された応答の整理に優れています、ところで!)。

また、キューを使用して、任意のスレッドで使用できるリソースのインスタンスを「パーク」することもできますが、一度に複数のスレッド間で共有することはできません(一部のDBAPIコンポーネントとのDB接続、他のカーソルとのカーソル接続など)-これにより、リラックスできますより多くのプーリングを優先する専用スレッド要件(キュー可能なリソースを必要とする要求を共有キューから取得するプールスレッドは、適切なキューからそのリソースを取得し、必要に応じて待機するなど)。

Twistedは、実際にはこのメヌエット(または場合によってはスクエアダンス)を整理するための良い方法です。遅延のおかげだけでなく、その堅実で堅実で拡張性の高いベースアーキテクチャのために、スレッドまたはサブプロセスを通常、単一のイベント駆動型スレッドでスレッドに値すると考えられるほとんどのことを行います。

しかし、私はツイストがすべての人のためではないことを認識しています-「リソースを専用またはプールし、キューを使い、ロックを必要とすることは絶対にしないでください」非同期イベント駆動型の方法論に頭を悩ませることができない場合でも使用され、私が今までに遭遇した他の広く適用可能なスレッド化アプローチよりも高い信頼性とパフォーマンスを提供します。

102
Alex Martelli

それはあなたが何をしようとしているかに依存しますが、私は標準ライブラリでthreadingモジュールを使用することには部分的に取り組んでいます。

from threading import Thread

def f():
    ...

def g(arg1, arg2, arg3=None):
    ....

Thread(target=f).start()
Thread(target=g, args=[5, 6], kwargs={"arg3": 12}).start()

等々。 Queueモジュールによって提供される同期キューを使用して、プロデューサー/コンシューマーをセットアップすることがよくあります

from Queue import Queue
from threading import Thread

q = Queue()
def consumer():
    while True:
        print sum(q.get())

def producer(data_source):
    for line in data_source:
        q.put( map(int, line.split()) )

Thread(target=producer, args=[SOME_INPUT_FILE_OR_SOMETHING]).start()
for i in range(10):
    Thread(target=consumer).start()
20
Eli Courtwright

カマエリアについては、上記の答えはここでの利点を実際にはカバーしていません。 Kamaeliaのアプローチは、スレッド、ジェネレーター、およびプロセスを単一システムで並行処理するための、完全ではない実用的な統合インターフェイスを提供します。

基本的に、受信ボックスと送信ボックスを持つ実行中の物のメタファーを提供します。メッセージを送信ボックスに送信します。接続すると、メッセージは送信ボックスから受信ボックスに流れます。このメタファー/ APIは、ジェネレーター、スレッド、またはプロセスを使用していても、他のシステムと話していても同じです。

「完璧ではない」部分は、まだ受信トレイと送信ボックスに構文糖が追加されていないためです(これは議論中ですが)-システムの安全性/有用性に焦点があります。

上記のベアスレッドを使用したプロデューサーコンシューマーの例をとると、これはカマエリアでは次のようになります。

Pipeline(Producer(), Consumer() )

この例では、これらがスレッド化されたコンポーネントであるかどうかは関係ありません。使用の観点から見た場合の唯一の違いは、コンポーネントのベースクラスです。ジェネレーターコンポーネントは、リストを使用して通信し、Queue.Queuesを使用してスレッド化されたコンポーネントを、os.pipesを使用してプロセスをベースに通信します。

しかし、このアプローチの背後にある理由は、バグのデバッグを難しくすることを難しくすることです。スレッド処理または共有メモリの同時実行において、直面する最大の問題は、共有データの更新が誤って破損することです。メッセージパッシングを使用することにより、バグのクラスoneを排除できます。

どこでもベアスレッドとロックを使用している場合、一般的に、コードを記述するときに間違いを犯さないと仮定して作業しています。私たち全員がそれを目指していますが、それが起こることは非常にまれです。ロック動作を1か所にまとめることにより、どこで問題が発生するかを単純化できます。 (コンテキストハンドラーは役立ちますが、コンテキストハンドラー以外での偶発的な更新には役立ちません)

明らかに、すべてのコードがメッセージパッシングおよび共有スタイルとして記述できるわけではないため、Kamaeliaには単純なソフトウェアトランザクションメモリ(STM)があります。これは、厄介な名前を持つ本当にすてきなアイデアです。いくつかの変数をチェックアウトし、それらを更新してコミットします。衝突した場合は、すすいで繰り返します。

関連リンク:

とにかく、私はそれが有用な答えであることを願っています。 FWIW、Kamaeliaのセットアップの背後にある主な理由は、犬を振る尾のないpythonシステムで、並行性をより安全かつ簡単にすることです。

私にとっても他のカマエリアの答えが修正された理由を理解することができます。 Kamaeliaの著者としては、これにもう少し関連性の高いコンテンツが含まれていることを願っていますが、熱意を見るのは素晴らしいことです:-)

それが私の言い方です。この答えは定義によってバイアスされているという警告を受け取ってください。いくつかのシステムを試してみて、どれがあなたに合っているかを見ることをお勧めします。 (これがスタックオーバーフローに不適切な場合も、申し訳ありません。このフォーラムは初めてです:-)

6
Michael Sparks

スレッドをまったく使用する必要がある場合は、Stackless Pythonのマイクロスレッド(タスクレット)を使用します。

オンラインゲーム全体(大規模マルチプレイヤー)は、Stacklessとそのマルチスレッドの原理に基づいて構築されています-オリジナルのゲームは、大規模なマルチプレイヤープロパティのゲームの速度を低下させるためです。

CPythonのスレッドは広く推奨されていません。 1つの理由は、実行の多くの部分でスレッド化をシリアル化するグローバルインタープリターロックであるGILです。私の経験では、この方法で高速アプリケーションを作成するのは本当に難しいということです。私のコーディング例では、スレッド処理がすべて遅く、1つのコアが使用されています(ただし、入力を待つことでパフォーマンスが向上するはずです)。

CPythonでは、可能であれば別のプロセスを使用します。

3
Juergen

本当に手を汚したい場合は、 ジェネレーターを使用してコルーチンを偽造する を試すことができます。おそらく、関連する作業の面では最も効率的ではありませんが、コルーチンは、プリエンプティブではなく、co-operativeマルチタスクの非常に細かい制御を提供します他の場所で見つかるマルチタスク。

あなたが見つける一つの利点は、概して、協調マルチタスクを使用するときにロックやミューテックスを必要としないことですが、私にとってより重要な利点は、「スレッド」間の切り替え速度がほぼゼロであることです。もちろん、Stackless Pythonはそのためにも非常に良いと言われています;そして、もしなければErlangがありますhavePythonになります。

おそらく、協調型マルチタスクの最大の欠点は、I/Oをブロックするための一般的な回避策がないことです。また、偽のコルーチンでは、スレッド内のスタックの最上位以外の「スレッド」を切り替えることができないという問題も発生します。

偽のコルーチンを使用してわずかに複雑なアプリケーションを作成した後、OSレベルでのプロセススケジューリングに関する作業に感謝します。

2
Mark Rushakoff