Pythonスレッドでのjoin()の使用は何ですか?


197

私はpythonのスレッディングを研究していて、出会いましたjoin()

作者は、スレッドがデーモンモードの場合、join()メインスレッドが終了する前にスレッドが終了できるように使用する必要があると述べました。

しかし、私は彼が使用しt.join()ていtなくても使用しているのを見ましたdaemon

サンプルコードはこれです

import threading
import time
import logging

logging.basicConfig(level=logging.DEBUG,
                    format='(%(threadName)-10s) %(message)s',
                    )

def daemon():
    logging.debug('Starting')
    time.sleep(2)
    logging.debug('Exiting')

d = threading.Thread(name='daemon', target=daemon)
d.setDaemon(True)

def non_daemon():
    logging.debug('Starting')
    logging.debug('Exiting')

t = threading.Thread(name='non-daemon', target=non_daemon)

d.start()
t.start()

d.join()
t.join()

t.join()デーモンではないので何を使うのか分からず、削除しても何も変わらない


13
タイトルの+1。「結合」は、(継続的にスレッドを作成/終了/破棄することによる)パフォーマンスの低下、GUIロックアップ(イベントハンドラーでの待機)、およびアプリのシャットダウンの失敗(割り込み不可能なスレッドの終了を待機)を促すように特別に設計されているようです。注-Pythonだけでなく、これはクロス言語のアンチパターンです。
マーティンジェームス

回答:


287

メカニズムを示すためのやや不器用なascii-art:join()はおそらくメインスレッドによって呼び出されます 別のスレッドから呼び出すこともできますが、不必要にダイアグラムが複雑になります。

join-callingはメインスレッドのトラックに配置する必要がありますが、スレッドの関係を表現してできるだけ単純にするために、代わりに子スレッドに配置することを選択します。

without join:
+---+---+------------------                     main-thread
    |   |
    |   +...........                            child-thread(short)
    +..................................         child-thread(long)

with join
+---+---+------------------***********+###      main-thread
    |   |                             |
    |   +...........join()            |         child-thread(short)
    +......................join()......         child-thread(long)

with join and daemon thread
+-+--+---+------------------***********+###     parent-thread
  |  |   |                             |
  |  |   +...........join()            |        child-thread(short)
  |  +......................join()......        child-thread(long)
  +,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,     child-thread(long + daemonized)

'-' main-thread/parent-thread/main-program execution
'.' child-thread execution
'#' optional parent-thread execution after join()-blocked parent-thread could 
    continue
'*' main-thread 'sleeping' in join-method, waiting for child-thread to finish
',' daemonized thread - 'ignores' lifetime of other threads;
    terminates when main-programs exits; is normally meant for 
    join-independent tasks

したがって、変更が表示されないのは、メインスレッドがの後で何もしないためですjoinjoinメインスレッドの実行フローに(のみ)関連があると言えます。

たとえば、一連のページを同時にダウンロードして1つの大きなページに連結する場合は、スレッドを使用して同時ダウンロードを開始できますが、最後のページ/スレッドが完了するまで待ってから、単一ページの組み立てを開始する必要があります。多くのうち。それはあなたが使用するときですjoin()


デーモン化されたスレッドがプログラムの実行をブロックせずにjoin()できることを確認してください。
Aviator45003 2015

@ Aviator45003:はい、次のようにタイムアウト引数を使用すると、デフォルトdemon_thread.join(0.0)join()は、デーモン化された属性に関係なくブロックされます。しかし、悪意のあるスレッドに参加すると、ほとんどの場合、問題が発生する可能性があります。私は今join()、デーモンスレッドの小さな図の呼び出しを削除することを検討しています...
Don Question

@DonQuestionでは、コードを最後に置くdaemon=True必要があるjoin()かどうかを設定する必要はありませんjoin()か?
Benyamin Jafari

@BenyaminJafari:はい。そうでない場合、daemon-threadだけが残っていると、main-thread(= program)が終了します。しかし、(python)デーモンスレッドの性質は、このバックグラウンドタスクがまだ実行されているかどうかをメインスレッドが気にしないことです。その問題を解決するために、私の答えでそれを詳しく説明する方法について考えます。コメントありがとうございます!
Don質問

最初のケースでは、main thread終了時に、プログラムはchild-thread(long)それ自体の実行を終了させることなく終了しますか(つまりchild-thread(long)、完全には実行されません)?
スカイツリー

65

ドキュメントから直接

join([timeout])スレッドが終了するまで待機します。これにより、join()メソッドが呼び出されたスレッドが(通常または未処理の例外によって)終了するまで、またはオプションのタイムアウトが発生するまで、呼び出しスレッドがブロックされます。

これは、tとを生成するメインスレッドがdt終了するまで終了を待機することを意味します。

プログラムが採用しているロジックによっては、メインスレッドが続行する前に、スレッドが終了するまで待機したい場合があります。

また、ドキュメントから:

スレッドには、「デーモンスレッド」としてフラグを設定できます。このフラグの重要性は、デーモンスレッドだけが残されると、Pythonプログラム全体が終了することです。

簡単な例を挙げましょう。

def non_daemon():
    time.sleep(5)
    print 'Test non-daemon'

t = threading.Thread(name='non-daemon', target=non_daemon)

t.start()

これで終わります:

print 'Test one'
t.join()
print 'Test two'

これは出力します:

Test one
Test non-daemon
Test two

ここで、マスタースレッドtprint、2回目の呼び出しまでスレッドが完了するのを明示的に待機します。

あるいは、これがあった場合:

print 'Test one'
print 'Test two'
t.join()

次の出力が得られます。

Test one
Test two
Test non-daemon

ここでは、メインスレッドでジョブを実行し、tスレッドが終了するのを待ちます。この場合、明示的な結合t.join()を削除することもでき、プログラムは暗黙的tに終了を待機します。


私はの違いを見ることができるようにあなたは私のコードにいくつかのchnageを作ることができますt.join()。soome sleepまたは何か他のものを加えることによって。現時点では、たとえ使用していてもしていなくても、プログラム内のあらゆる変化を見ることができます。私が使用している場合、私はその終了を確認することができますdamemonためではなく、d.join()これを私は()d.join使用していないとき、私は表示されません
user192362127

34

このスレッドをありがとう-それも私を助けてくれた。

今日、.join()について何かを学びました。

これらのスレッドは並行して実行されます。

d.start()
t.start()
d.join()
t.join()

そしてこれらは順番に実行されます(私が望んだものではありません):

d.start()
d.join()
t.start()
t.join()

特に、私は賢く整頓しようとしていました:

class Kiki(threading.Thread):
    def __init__(self, time):
        super(Kiki, self).__init__()
        self.time = time
        self.start()
        self.join()

これでうまくいきます!ただし、順次実行されます。self.start()を__ init __に置くことはできますが、self.join()はできません。これはすべてのスレッドが開始されたに行う必要があります。

join()は、メインスレッドがスレッドの終了を待機する原因になります。それ以外の場合、スレッドはすべて単独で実行されます。

つまり、join()をメインスレッドの「ホールド」と考える1つの方法は、メインスレッドが続行する前に、スレッドのスレッドを解除し、メインスレッドで順次実行することです。これにより、メインスレッドが前進する前にスレッドが完了します。これは、join()を呼び出す前にスレッドが既に完了している場合でも問題ないことを意味します。メインスレッドは、join()が呼び出されるとただちに解放されるだけです。

実際のところ、メインスレッドがスレッドdが終了するまでd.join()で待機し、その後スレッドdがt.join()に移動するようになっています。

実際、非常に明確にするために、次のコードを検討してください。

import threading
import time

class Kiki(threading.Thread):
    def __init__(self, time):
        super(Kiki, self).__init__()
        self.time = time
        self.start()

    def run(self):
        print self.time, " seconds start!"
        for i in range(0,self.time):
            time.sleep(1)
            print "1 sec of ", self.time
        print self.time, " seconds finished!"


t1 = Kiki(3)
t2 = Kiki(2)
t3 = Kiki(1)
t1.join()
print "t1.join() finished"
t2.join()
print "t2.join() finished"
t3.join()
print "t3.join() finished"

この出力を生成します(printステートメントが互いにどのようにスレッド化されているかに注意してください。)

$ python test_thread.py
32   seconds start! seconds start!1

 seconds start!
1 sec of  1
 1 sec of 1  seconds finished!
 21 sec of
3
1 sec of  3
1 sec of  2
2  seconds finished!
1 sec of  3
3  seconds finished!
t1.join() finished
t2.join() finished
t3.join() finished
$ 

t1.join()がメインスレッドを保持しています。3つのスレッドはすべて、t1.join()が終了する前に完了し、メインスレッドは印刷、t2.join()、印刷、t3.join()、印刷の順に実行されます。

修正は歓迎します。スレッディングも初めてです。

(注:興味がある場合は、DrinkBotのコードを書いています。原料ポンプを順次ではなく同時に実行するためにスレッド化する必要があります-各ドリンクを待つ時間が短くなります。)


ねえ、私もpythonスレッドに慣れていないし、メインスレッドについて混乱しています。最初のスレッドはメインスレッドですか、そうでない場合は、私を案内してください。
Rohit Khatri

メインスレッドはプログラム自体です。各スレッドはそこから分岐されます。次に、それらは結合されます-コマンドjoin()で、プログラムはスレッドが終了するまで待機してから実行を続けます。
Kiki Jewell 2017年

15

メソッドjoin()

join()メソッドが呼び出されたスレッドが終了するまで、呼び出しスレッドをブロックします。

ソース:http : //docs.python.org/2/library/threading.html


14
ジョインの使用は何ですか?ドキュメントを言い換えるだけでなく、OPの質問を参照してください
Don Question

@DonQuestionを使用せずに非デーモンスレッドにsleep.timer(20)を追加しようとしてもt.join()、プログラムは終了する前に待機します。t.join()私のコードではここでの使用は見られません
user192362127 '26 / 02/26

詳細については、私の回答を参照してください。非デーモンのsleep.timerに関して->デーモンスレッドはその親スレッドの存続期間から切り離されているため、親/兄弟スレッドはデーモン化されたスレッドの存続期間の影響を受けず、その逆も同様です。
Don Question

2
「結合」と「ブロック」の用語は不可解です。「ブロックされている」とは、呼び出し側プロセスがまだ実行する必要のある処理を「ブロック」していることを示していますが、実際には、それ以上は終了しない(OSに戻る)のがブロックされているだけです。同様に、メインスレッドが子スレッドを呼び出して「結合」(つまり終了)することはそれほど明白ではありません。それでは、ドンQ、説明ありがとうございます。
RolfBly 2014年

5

シンプルな理解、

結合あり-インタープリターは、プロセスが完了する終了するまで待機します

>>> from threading import Thread
>>> import time
>>> def sam():
...   print 'started'
...   time.sleep(10)
...   print 'waiting for 10sec'
... 
>>> t = Thread(target=sam)
>>> t.start()
started

>>> t.join() # with join interpreter will wait until your process get completed or terminated
done?   # this line printed after thread execution stopped i.e after 10sec
waiting for 10sec
>>> done?

参加なし-インタプリタはプロセスが終了するまで待機しません、

>>> t = Thread(target=sam)
>>> t.start()
started
>>> print 'yes done' #without join interpreter wont wait until process get terminated
yes done
>>> waiting for 10sec

1

join(t)非デーモンスレッドとデーモンスレッドの両方に対して関数を作成する場合、メインスレッド(またはメインプロセス)はt数秒待機してから、独自のプロセスでさらに作業を進めることができます。t待機時間の数秒間、子スレッドは両方とも、テキストを印刷するなど、実行できることを実行する必要があります。t数秒後、デーモン以外のスレッドがまだジョブを完了せず、メインプロセスがジョブを完了した後もスレッドを完了できる場合は、デーモンスレッドの場合、機会ウィンドウを逃しました。ただし、Pythonプログラムの終了後に最終的には停止します。何か問題がある場合は修正してください。


1

Python 3.xでは、join()を使用してスレッドをメインスレッドに結合します。つまり、join()を特定のスレッドに使用すると、結合したスレッドの実行が完了するまでメインスレッドの実行が停止します。

#1 - Without Join():
import threading
import time
def loiter():
    print('You are loitering!')
    time.sleep(5)
    print('You are not loitering anymore!')

t1 = threading.Thread(target = loiter)
t1.start()
print('Hey, I do not want to loiter!')
'''
Output without join()--> 
You are loitering!
Hey, I do not want to loiter!
You are not loitering anymore! #After 5 seconds --> This statement will be printed

'''
#2 - With Join():
import threading
import time
def loiter():
    print('You are loitering!')
    time.sleep(5)
    print('You are not loitering anymore!')

t1 = threading.Thread(target = loiter)
t1.start()
t1.join()
print('Hey, I do not want to loiter!')

'''
Output with join() -->
You are loitering!
You are not loitering anymore! #After 5 seconds --> This statement will be printed
Hey, I do not want to loiter! 

'''

0

この例は、.join()アクションを示しています。

import threading
import time

def threaded_worker():
    for r in range(10):
        print('Other: ', r)
        time.sleep(2)

thread_ = threading.Timer(1, threaded_worker)
thread_.daemon = True  # If the main thread kills, this thread will be killed too. 
thread_.start()

flag = True

for i in range(10):
    print('Main: ', i)
    time.sleep(2)
    if flag and i > 4:
        print(
            '''
            Threaded_worker() joined to the main thread. 
            Now we have a sequential behavior instead of concurrency.
            ''')
        thread_.join()
        flag = False

でる:

Main:  0
Other:  0
Main:  1
Other:  1
Main:  2
Other:  2
Main:  3
Other:  3
Main:  4
Other:  4
Main:  5
Other:  5

            Threaded_worker() joined to the main thread. 
            Now we have a sequential behavior instead of concurrency.

Other:  6
Other:  7
Other:  8
Other:  9
Main:  6
Main:  7
Main:  8
Main:  9

0

メインスレッド(または他のスレッド)が他のスレッドに参加する理由はいくつかあります

  1. スレッドがいくつかのリソースを作成または保持(ロック)している可能性があります。参加呼び出しスレッドは、その代わりにリソースをクリアできる可能性があります

  2. join()は、呼び出されたスレッドが終了した後も、結合呼び出しスレッドが継続するための自然なブロッキング呼び出しです。

Pythonプログラムが他のスレッドに参加しない場合でも、Pythonインタープリターはその代わりにデーモン以外のスレッドに参加します。


-2

「join()を使用する目的は何ですか?」あなたは言う。本当に、それは「プログラムを終了するとpythonとOSがファイルを閉じるので、ファイルを閉じる方法は何ですか?」と同じ答えです。

それは単に良いプログラミングの問題です。スレッドが実行されないようにするコード内のポイントでスレッドをjoin()する必要あります。これは、自分のコードに干渉するためにスレッドが実行されていないことを確実に確認する必要があるか、またはより大きなシステム。

join()が必要とする可能性がある追加の時間のために、「コードが応答を返すのを遅らせたくない」と言うかもしれません。これは一部のシナリオでは完全に有効な場合がありますが、コードが「PythonとOSがクリーンアップするために残されたまま」であることを考慮する必要があります。パフォーマンス上の理由でこれを行う場合は、その動作を文書化することを強くお勧めします。これは、他の人が利用することが期待されるライブラリ/パッケージを構築している場合に特に当てはまります。

パフォーマンス上の理由以外に、join()をしない理由はありません。あなたのコードがそれをうまく実行する必要はないと私は主張します。


6
スレッドのクリーンアップについてあなたが言うことは正しくありません。threading.Thread.join()のソースコードを見てください。その関数が行うことは、ロックを待ってから戻ることだけです。実際にクリーンアップされるものはありません。
コリン

1
@Collin-スレッド自体がリソースを保持している可能性があります。その場合、インタプリタとOSは実際に「残骸」をクリーンアップする必要があります。
qneill

1
もう一度、threading.Thread.join()のソースコードを見てください。リソースの収集をトリガーするものは何もありません。
Collin

リソースを保持しているスレッド化モジュールは必ずしも(そして言うまでもなく)スレッド自体ではありません。join()を使用すると、スレッドがやりたいことを完了するのを待つことになります。これには、リソースの割り当てと解放が含まれる場合があります。
Chris Cogdon、2015

2
待機するかどうかは、スレッドが保持するリソースが解放されるタイミングには影響しません。なぜこれをと結び付けているのかわかりませんjoin()
Collin
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.