2012-09-18 9 views
7

複数の送信者と受信者が存在する場合、なぜPipesが安全でないと言われているのか分かりません。Pythonマルチプロセッシングパイプが安全でないのはなぜですか?

この場合、Queuesを使用して次のコードをコードに変換する方法はありますか? Queues閉じてもEOFErrorが投げられないので、私のプロセスは停止できません。無限に 'Poison'メッセージを送信して停止するように指示する必要があります(この方法では、すべてのプロセスに少なくとも1つの毒が与えられます)。

私がそうでないと決めるまで、私はパイプp1を開いたままにしておきたいと思います(ここでは10のメッセージを送ったときです)。


from multiprocessing import Pipe, Process 
from random import randint, random 
from time import sleep 

def job(name, p_in, p_out): 
    print(name + ' starting') 
    nb_msg = 0 
    try: 
     while True: 
      x = p_in.recv() 
      print(name + ' receives ' + x) 
      nb_msg = nb_msg + 1 
      p_out.send(x) 
      sleep(random()) 
    except EOFError: 
     pass 
    print(name + ' ending ... ' + str(nb_msg) + ' message(s)') 

if __name__ == '__main__': 
    p1_in, p1_out = Pipe() 
    p2_in, p2_out = Pipe() 

    proc = [] 

    for i in range(3): 
     p = Process(target=job, args=(str(i), p1_out, p2_in)) 
     p.start() 
     proc.append(p) 

    for x in range(10): 
     p1_in.send(chr(97+x)) 
    p1_in.close() 
    for p in proc: 
     p.join() 
    p1_out.close() 
    p2_in.close() 

    try: 
     while True: 
      print(p2_out.recv()) 
    except EOFError: 
     pass 

    p2_out.close() 

答えて

13

本質的に、問題はPipeプラットフォーム定義パイプオブジェクトの周りに薄いラッパーであることです。 recvは、完全なPythonオブジェクトが得られるまで単純にバイトのバッファを受け取ります。 2つのスレッドまたはプロセスが同じパイプ上でrecvを使用すると、読み込みがインターリーブされ、各プロセスにピクルされたオブジェクトの半分が残り、データが破損する可能性があります。 Queueは、プロセス間の適切な同期を行いますが、複雑さは増します。

multiprocessingドキュメントはそれを置くとして2つのプロセス(またはスレッド)から読み取るか、同時にパイプの同じ端部に書き込みしようとした場合、パイプ内のデータが破損することが

注意を。もちろん、パイプの異なる端部を同時に使用するプロセスからの腐敗のリスクはありません。

毒の丸薬を無限に送る必要はありません。一人一人が必要なものすべてです。各作業者は、終了する前に毒ピルを1つだけピックアップするので、作業員が何らかの理由でメッセージを見逃す危険はありません。

「ワーカープロセス」モデルを再実装する代わりに、multiprocessing.Poolを使用することを検討する必要があります。Poolには、複数のスレッド間での作業の分散を容易にする方法が多数あります。

+0

パイプの 'recv'と' send'を使うときに 'multiprocessing.Lock()'を使うとどうなりますか?それは安全になる(効率的になる)のだろうか? – thuzhf

+0

これを行うと、基本的に 'Queue' - ' multiprocessing.Queue'はロックのペア(各方向に1つ)が付いた 'Pipe'です。したがって、安全で合理的​​に効率的ですが、ホイールを再発明することもできます。なぜなら、単に「キュー」を使用するだけではないからです。 – nneonneo

7

なぜ複数の送信者と受信者がいる場合にパイプが安全でないと言われているのか分かりません。

ソースAとBから同時にパイプに水を入れることを検討してください。パイプの反対側では、水のどの部分がAまたはBから来たのかを知ることは不可能でしょうか? :)

パイプはバイトレベルでデータストリームを転送します。その上に通信プロトコルがなければ、メッセージが何であるかを知らないため、メッセージの整合性を保証できません。したがって、複数の送信者がいるパイプを使用するのは「安全でない」だけではありません。これは大きな設計上の欠陥であり、通信の問題を引き起こす可能性が最も高いです。

ただし、キューは上位レベルで実装されています。これらは、メッセージ(または抽象的なオブジェクト)を通信するように設計されています。キューは、メッセージ/オブジェクトを自己保持するために作成されます。複数のソースがオブジェクトをキューに入れることができ、複数のコンシューマがこれらのオブジェクトをプルすることができますが、ユニットとしてキューに入ったものもユニットとして出てくることを100%確信しています。かなり長い間の後

編集:

私はバイトストリームでは、すべてのバイトが送られたのと同じ順序(保証)で取得されることを追加する必要があります。複数の送信者の問題は、送信順序(入力の順番)が既に不明瞭またはランダムである可能性があることです。つまり、複数のストリームが予測できない形で混在する可能性があります。

共通のキュー実装では、複数の送信者が存在する場合でも、単一のメッセージがそのまま維持されます。メッセージは、送信済みの順番で取得されます。しかし、複数の競合する送信者が存在し、それ以上の同期メカニズムがない場合でも、入力メッセージの順序についての保証はありません。

関連する問題