Pythonマルチプロセッシングモジュールの.join()メソッドは正確には何をしているのですか?


110

Python Multiprocessingについて(PMOTWの記事から)学び、join()メソッドが正確に何をしているのかについての説明が必要です。

2008年の古いチュートリアルp.join()では、以下のコードの呼び出しがないと、「子プロセスはアイドル状態で終了せず、ゾンビになるため、手動で強制終了する必要がある」と述べています。

from multiprocessing import Process

def say_hello(name='world'):
    print "Hello, %s" % name

p = Process(target=say_hello)
p.start()
p.join()

私はプリントアウトの追加PIDだけでなくtime.sleep、自分自身で処理を終了するをテストし、私の知る限りを:

from multiprocessing import Process
import sys
import time

def say_hello(name='world'):
    print "Hello, %s" % name
    print 'Starting:', p.name, p.pid
    sys.stdout.flush()
    print 'Exiting :', p.name, p.pid
    sys.stdout.flush()
    time.sleep(20)

p = Process(target=say_hello)
p.start()
# no p.join()

20秒以内:

936 ttys000    0:00.05 /Library/Frameworks/Python.framework/Versions/2.7/Reso
938 ttys000    0:00.00 /Library/Frameworks/Python.framework/Versions/2.7/Reso
947 ttys001    0:00.13 -bash

20秒後:

947 ttys001    0:00.13 -bash

動作は同じp.join()で、ファイルの最後に追加されます。今週のPythonモジュールは、モジュールの非常に読みやすい説明を提供しています。「プロセスがその作業を完了して終了するまで待機するには、join()メソッドを使用します。」しかし、少なくともOS Xはそれを行っていたようです。

メソッドの名前についても疑問に思っています。.join()メソッドはここに何かを連結していますか?それはそれの終わりでプロセスを連結していますか?または、Pythonのネイティブ.join()メソッドと名前を共有するだけですか?


2
私の知る限り、メインスレッドを保持し、子プロセスが完了するのを待ってから、メインスレッドのリソースに参加します。ほとんどの場合、クリーンな終了を行います。
abhishekgarg 2014

ああ、それは理にかなっています。それで、実際CPU, Memory resourcesは親プロセスから分離されておりjoin、子プロセスが完了した後、再び戻されますか?
MikeiLL 2014

はい、それはそのことです。したがって、それらを結合しない場合、子プロセスが終了すると、子プロセスは無効またはデッドプロセスとして存在します
abhishekgarg

@abhishekgargそうではありません。メインプロセスが完了すると、子プロセスは暗黙的に結合されます。
ダノ2014

@dano、私はpythonも学習しています。テストで見つけたものを共有しただけです。テストでは、終わりのないメインプロセスがあったため、これらの子プロセスが無効であると見なされたのはおそらくそのためです。
abhishekgarg 2014

回答:


125

このjoin()メソッドは、threadingまたはとともに使用される場合multiprocessing、関連しstr.join()ていません-実際には何も連結していません。むしろ、「この[スレッド/プロセス]が完了するのを待つ」という意味です。この名前joinが使用されているのは、multiprocessingモジュールのAPIがモジュールのAPIと同様に見えるように設計されてthreadingおり、threadingモジュールがjoinそのThreadオブジェクトに使用しているためです。join「スレッドが完了するのを待つ」という用語を使用することは、多くのプログラミング言語で一般的であるため、Pythonでも同様に採用されました。

ここで、呼び出しの有無にかかわらず20秒の遅延が表示されるのjoin()は、デフォルトでは、メインプロセスが終了する準備ができると、join()実行中のすべてのmultiprocessing.Processインスタンスが暗黙的に呼び出されるためです。これはmultiprocessingドキュメントでは明確に述べられていませんが、プログラミングガイドラインのセクションで言及されています。

非デーモンプロセスが自動的に結合されることにも注意してください。

プロセスを開始する前ににdaemonフラグを設定することで、この動作をオーバーライドできます。ProcessTrue

p = Process(target=say_hello)
p.daemon = True
p.start()
# Both parent and child will exit here, since the main process has completed.

その場合、メインプロセスが完了するとすぐに子プロセスが終了します

デーモン

プロセスのデーモンフラグ。ブール値。これは、start()が呼び出される前に設定する必要があります。

初期値は作成プロセスから継承されます。

プロセスが終了すると、デーモンのすべての子プロセスを終了しようとします。


6
それはp.daemon=True「メインプログラムの終了をブロックせずに実行されるバックグラウンドプロセスを開始する」ためのものであることを理解していました。しかし、「メインプログラムが終了する前にデーモンプロセスが自動的に終了する」場合、その使用法は正確には何ですか?
MikeiLL 2014

8
@MikeiLL基本的に、親プロセスが実行されている限り、バックグラウンドで実行したいすべてのものですが、メインプログラムを終了する前に正常にクリーンアップする必要はありません。おそらく、ソケットまたはハードウェアデバイスからデータを読み取り、そのデータをキュー経由で親にフィードバックするか、なんらかの目的でバックグラウンドで処理するワーカープロセスでしょうか。一般に、daemonic子プロセスを使用することは安全ではありません。なぜなら、プロセスは、開いているリソースをクリーンアップすることを許可せずに終了するためです。
dano 2014

7
@MikeiLLより良い方法は、メインプロセスを終了する前に、クリーンアップして終了するように子供に通知することです。親が終了するときにデーモンの子プロセスを実行したままにしておくのは理にかなっていると思うかもしれませんが、multiprocessingAPIはthreadingできる限りAPI を模倣するように設計されていることに注意してください。鬼神のthreading.Threadオブジェクトはそう鬼神の、すぐにメインスレッドが終了などとして終了されるmultiprocesing.Processオブジェクトが同じように振る舞います。
dano 2014

38

がないjoin()場合、メインプロセスは子プロセスが完了する前に完了できます。どのような状況でゾンビ主義につながるのかはわかりません。

の主な目的はjoin()、メインプロセスが子プロセスの作業に依存する処理を実行する前に、子プロセスが完了していることを確認することです。

の語源join()は、それがの逆であることですfork。これは、子プロセスを作成するためのUnixファミリのオペレーティングシステムでは一般的な用語です。単一のプロセスはいくつかに「フォーク」し、次に「結合」して1つに戻します。


2
これは、名前を使用していますjoin()ので、join()待つために使用されるものですthreading.Thread完全にオブジェクト、およびmultiprocessingAPIを模倣することを意味しているthreading可能な限りAPI。
dano 14

2番目のステートメントは、現在のプロジェクトで私が処理している問題に対処します。
MikeiLL 2014

メインスレッドがサブプロセスの完了を待機する部分を理解していますが、非同期実行の目的をそれほど損ないませんか?独立して(サブタスクまたはプロセス)実行を終了することになっていますか?
アプルバクンクロル

1
@ApurvaKunkulol使用方法によって異なりますjoin()が、メインスレッドがサブスレッドの作業の結果を必要とする場合に必要です。たとえば、何かをレンダリングしていて、最終的な画像の1/4を4つのサブプロセスのそれぞれに割り当て、完了時に画像全体を表示したい場合などです。
Russell Borogove

@RussellBorogoveああ!わかった。次に、非同期アクティビティの意味が少し異なります。これは、サブプロセスがメインスレッドと同時にタスクを実行するためのものであり、メインスレッドもサブプロセスを単に待機するのではなく、その役割を果たしているという事実のみを意味する必要があります。
アプルバクンクロル

12

私は何をするのか詳しく説明するつもりはありませんjoinが、ここに語源とその背後にある直感があります。これは、その意味をより簡単に思い出すのに役立つはずです。

アイデアは、1つがマスター、残りのワーカー(または「スレーブ」)である複数のプロセスへの実行の「フォーク」です。ワーカーが完了すると、マスターに「参加」するため、シリアル実行が再開されます。

このjoinメソッドにより、マスタープロセスはワーカーが参加するのを待機します。これはマスターで発生する実際の動作であるため、メソッドは「待機」と呼ばれる方がよいかもしれません(POSIXスレッドでは「結合」とも呼ばれますが、POSIXではそれが呼び出されます)。参加は、スレッドが適切に連携することの効果としてのみ発生し、マスターが行うことではありません

「フォーク」および「結合」という名前は、1963年以降、マルチプロセッシングでこの意味で使用されています。


したがって、ある意味では、この単語joinの使用は、連結を参照する際の使用の前に、他の方法とは対照的に行われた可能性があります。
MikeiLL 2014

1
マルチプロセッシングでの使用から派生した連結での使用はほとんどありません。どちらの感覚も、平易な英語の単語の感覚とは別に派生します。
ラッセルボロゴーブ2014

2

join()ワーカープロセスの終了を待機するために使用されます。を使用する前に、close()またはを呼び出す必要があります。terminate()join()

@Russellが述べたように、joinfork(サブプロセスを生成する)の反対です。

参加するには、実行する必要があります。close()これを実行すると、プールに送信されるタスクがなくなり、すべてのタスクが完了すると終了します。または、terminate()すべてのワーカープロセスをすぐに停止することで、実行を終了します。

"the child process will sit idle and not terminate, becoming a zombie you must manually kill" これは、メイン(親)プロセスが終了しても子プロセスがまだ実行中であり、一度完了すると、終了ステータスを返す親プロセスがない場合に可能です。


2

このjoin()呼び出しにより、すべてのマルチプロセッシングプロセスが完了する前に、コードの後続の行が呼び出されないことが保証されます。

たとえばjoin()、次のコードを使用しない場合、restart_program()プロセスが完了する前でも次のコードが呼び出されます。これは非同期に似ており、私たちが望むものではありません(試すことができます)。

num_processes = 5

for i in range(num_processes):
    p = multiprocessing.Process(target=calculate_stuff, args=(i,))
    p.start()
    processes.append(p)
for p in processes:
    p.join() # call to ensure subsequent line (e.g. restart_program) 
             # is not called until all processes finish

restart_program()

0

プロセスが作業を完了して終了するまで待機するには、join()メソッドを使用します。

そして

注バックグラウンドマシンにオブジェクトのステータスを更新して終了を反映させる時間を与えるために、プロセスを終了した後にjoin()することが重要です。

これは私が理解するのに役立つ良い例です:ここに

個人的に気付いたのは、子供がjoin()メソッドを使用してプロセスを完了するまでメインプロセスが一時停止することmultiprocessing.Process()でした。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.