파이썬 스레딩에서 join ()을 사용하는 것은 무엇입니까?


197

나는 파이썬 스레딩을 공부하고 있었고 건너왔다 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 'Join'은 성능 저하 (계속 스레드 생성 / 종료 / 파기), GUI 잠금 (이벤트 핸들러에서 대기) 및 앱 종료 실패 (무정전 스레드 종료를 기다리는 중)를 장려하도록 특별히 설계된 것으로 보입니다. 참고-파이썬뿐만 아니라 언어 간 안티 패턴입니다.
Martin James

답변:


287

메커니즘을 설명하기 위해 다소 어색한 예술 작품 : join()아마도 메인 스레드에 의해 호출됩니다. 다른 스레드에서 호출 할 수도 있지만 다이어그램을 불필요하게 복잡하게 만듭니다.

join호출은 주 스레드의 트랙에 배치해야하지만 스레드 관계를 표현하고 가능한 한 간단하게 유지하려면 대신 자식 스레드에 배치하도록 선택합니다.

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

따라서 변경 사항이 표시되지 않는 이유는 메인 스레드가 join. join메인 스레드의 실행 흐름과 관련이 있다고 말할 수 있습니다.

예를 들어, 여러 페이지를 동시에 다운로드하여 하나의 큰 페이지로 연결하려는 경우 스레드를 사용하여 동시 다운로드를 시작할 수 있지만 단일 페이지를 조립하기 전에 마지막 페이지 / 스레드가 완료 될 때까지 기다려야합니다 많은 중. 그때 사용 join()합니다.


프로그램 실행을 차단하지 않고 데몬 처리 된 스레드를 join () 할 수 있는지 확인하십시오.
Aviator45003

Aviator45003 @ : 예, 같은 시간 제한 인수를 사용하여 : demon_thread.join(0.0), join()daemonized 속성에 관계없이 차단 기본입니다. 그러나 악마 실에 합류하면 문제가 발생할 가능성이 높습니다! join()데몬 스레드에 대한 작은 다이어그램에서 호출 을 제거하는 것을 고려하고 있습니다 .
Don Question

우리가 설정 한 경우 @DonQuestion 따라서는 daemon=True우리는 필요하지 않습니다 join()우리가해야 할 경우 join()코드의 끝에서?
Benyamin Jafari

@BenyaminJafari : 예. 그렇지 않으면 데몬 스레드 만 남으면 main-thread (= program)가 종료됩니다. 그러나 (파이썬) 데몬 스레드의 특성은 메인 백그라운드가이 백그라운드 작업이 여전히 실행 중인지 신경 쓰지 않는다는 것입니다. 나는 내 대답에서 그것을 자세히 설명하고 그 문제를 해결하는 방법에 대해 생각할 것입니다. 귀하의 의견에 감사드립니다!
Don Question

첫 번째 경우, main thread완료되면 프로그램이 child-thread(long)스스로 실행을 끝내지 않고 종료 child-thread(long)됩니까 (즉, 완전히 완료되지 않았습니까)?
skytree

65

문서 에서 바로

join ([timeout]) 스레드가 종료 될 때까지 기다립니다. 이것은 join () 메소드가 호출 된 스레드가 정상적으로 또는 처리되지 않은 예외를 통해 종료 될 때까지 또는 선택적 시간 종료가 발생할 때까지 호출 스레드를 차단합니다.

이 수단 메인 쓰레드있는 급부상 그 td에 대한 대기 t가 끝날 때까지 완료합니다.

프로그램에서 사용하는 논리에 따라 메인 스레드가 계속되기 전에 스레드가 완료 될 때까지 기다릴 수 있습니다.

또한 문서에서 :

스레드는 "데몬 스레드"로 표시 될 수 있습니다. 이 플래그의 의미는 데몬 스레드 만 남으면 전체 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

여기서 마스터 스레드는 t스레드가 print두 번째 호출 될 때까지 스레드가 완료 될 때까지 명시 적으로 대기합니다 .

또는 우리가 이것을 가지고 있다면 :

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

이 결과를 얻을 수 있습니다 :

Test one
Test two
Test non-daemon

여기서 우리는 메인 스레드에서 작업을 수행 한 다음 t스레드가 완료 될 때까지 기다립니다 . 이 경우 명시 적 결합을 제거 할 수도 t.join()있으며 프로그램은 암시 적 t으로 완료를 기다립니다 .


난의 차이를 볼 수 있도록 내 코드에 약간의 chnage를 만들 수 있습니다 t.join(). 수면 또는 다른 것을 추가하여. 순간에 나는 그것을 사용하거나 사용하지 않아도 프로그램에서 chnage를 볼 수 있습니다. 그러나 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 ()을 "hold"로 생각하는 한 가지 방법은 메인 스레드를 계속하기 전에 스레드를 스레드 해제하고 메인 스레드에서 순차적으로 실행하는 것입니다. 메인 스레드가 앞으로 이동하기 전에 스레드가 완료되었는지 확인합니다. join ()을 호출하기 전에 스레드가 이미 완료된 경우 괜찮습니다. 기본 스레드는 join ()이 호출 될 때 즉시 해제됩니다.

사실, 메인 스레드가 스레드 d가 t.join ()으로 이동하기 전에 완료 될 때까지 d.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"

이 출력을 생성합니다 (인쇄 문이 서로 스레드되는 방식에 유의하십시오).

$ 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 ()이 메인 스레드를 잡고 있습니다. t1.join ()이 끝나기 전에 3 개의 스레드가 모두 완료되고 메인 스레드가 인쇄를 실행 한 다음 t2.join ()을 인쇄 한 다음 t3.join ()을 인쇄하고 인쇄합니다.

정정을 환영합니다. 또한 스레딩을 처음 사용합니다.

(참고 : 관심이 있으시면 DrinkBot 용 코드를 작성 중이며 성분 펌프를 순차적으로 실행하지 않고 동시에 실행하기 위해 스레딩이 필요합니다. 각 음료를 기다리는 시간이 짧습니다.)


이봐, 나는 또한 파이썬 스레딩을 처음 사용하고 메인 스레드에 대해 혼란스러워했다. 첫 번째 스레드는 메인 스레드입니까? 그렇지 않다면 나를 안내 해주십시오?
Rohit Khatri

주요 스레드는 프로그램 자체입니다. 각 스레드는 거기에서 분기됩니다. 그러면 join () 명령에서 스레드가 계속 실행되기 전에 스레드가 완료 될 때까지 프로그램이 대기하기 때문에 다시 결합됩니다.
Kiki Jewell

15

join () 메소드

join () 메소드가 호출 된 스레드가 종료 될 때까지 호출 스레드를 차단합니다.

출처 : http://docs.python.org/2/library/threading.html


14
그래서 join의 사용법은 무엇입니까? OP 질문을 참조하십시오. 문서를 간단히 말하면 안됩니다.
Don Question

@ DonQuestion 심지어 사용하지 않고 데몬이 아닌 스레드에서 sleep.timer (20) 추가를 시도했지만 t.join()프로그램은 여전히 ​​종료 전에 기다립니다. t.join()내 코드에서 여기를 사용하지 않습니다
user192362127

자세한 설명은 내 답변을 참조하십시오. non-demon의 sleep.timer와 관련하여-> 악마 스레드는 부모 스레드의 수명과 분리되어 있으므로 부모 / 형제 스레드는 악마 스레드의 수명에 영향을받지 않으며 그 반대도 마찬가지입니다. .
돈 질문

2
'가입'과 '차단'용어는 당황 스럽습니다. '차단됨'은 호출 프로세스가 여전히 수행해야하는 많은 작업을 수행하는 것이 '차단됨'을 의미하지만 실제로는 더 이상 종료 (OS로 돌아 가기)되지 않도록 차단되었습니다. 같은 토큰으로, 자식 스레드를 호출하여 '결합'(즉, 종료)하는 메인 스레드가 있다는 것은 분명하지 않습니다. 따라서 Don Q, 설명해 주셔서 감사합니다.
RolfBly

5

간단한 이해,

with join-통역사가 프로세스가 완료 되거나 종료 될 때까지 기다립니다.

>>> 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?

join-interpreter 없이는 프로세스가 종료 될 때까지 기다리지 않습니다 .

>>> 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초, 비 데몬 스레드가 여전히 작업을 완료하지 않았고, 주요 프로세스가 작업을 완료 한 후 여전히 그것을 끝낼 수 있지만, 데몬 스레드, 그냥 그 기회의 창을 놓친 경우. 그러나 파이썬 프로그램이 종료되면 결국 죽을 것입니다. 문제가 있으면 수정 해주세요.


1

파이썬 3.x에서 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 ()은 호출 된 스레드가 종료 된 후에도 계속해서 결합 호출 스레드에 대한 자연적인 차단 호출입니다.

파이썬 프로그램이 다른 스레드를 조인하지 않으면 파이썬 인터프리터는 여전히 데몬 이외의 스레드를 대신합니다.


-2

"join () 사용의 용도는 무엇입니까?" 당신은 말합니다. 실제로, "파이썬과 OS가 내 프로그램을 종료 할 때 파일을 닫을 것이기 때문에 파일 닫기 사용법은 무엇입니까?"와 같은 대답입니다.

단순히 좋은 프로그래밍 문제입니다. 스레드가 더 이상 실행 되지 않아야하는 코드 포인트에서 스레드를 join () 해야합니다. 스레드가 자신의 코드를 방해하기 위해 실행되고 있지 않은지 또는 더 큰 시스템.

join ()에 필요할 수있는 추가 시간 때문에 "코드 응답이 늦어지는 것을 원하지 않습니다"라고 말할 수 있습니다. 이것은 일부 시나리오에서 완벽하게 유효 할 수 있지만 이제는 코드가 "파이썬과 OS를 정리하기 위해 혼란을 겪고 있음"을 고려해야합니다. 성능상의 이유로이 작업을 수행하는 경우 해당 동작을 문서화하는 것이 좋습니다. 다른 사람들이 활용할 것으로 예상되는 라이브러리 / 패키지를 구축하는 경우 특히 그렇습니다.

이 성능상의 이유로 이외의 (가입 할 이유가) 없다, 나는 당신의 코드가 수행 할 필요가 없다는 주장 잘.


6
스레드 정리에 대해 말하는 것은 사실이 아닙니다. threading.Thread.join ()의 소스 코드를 살펴보십시오. 그 기능은 잠금을 기다린 다음 돌아 오는 것입니다. 실제로 아무것도 청소되지 않습니다.
Collin

1
@Collin-스레드 자체가 리소스를 보유하고있을 수 있으며이 경우 인터프리터와 OS는 실제로 "크래프트 (cruft)"를 정리해야합니다.
qneill

1
다시 threading.Thread.join ()의 소스 코드를보십시오. 거기에 자원 수집을 트리거하는 것은 없습니다.
Collin

리소스를 보유하고있는 스레딩 모듈은 반드시 필요하지는 않지만 스레드 자체는 아닙니다. join ()을 사용하면 스레드가 원하는 작업을 마치고 리소스 할당 및 해제가 포함될 수 있습니다.
Chris Cogdon

2
대기 여부는 스레드가 보유한 자원이 해제 될 때 영향을 미치지 않습니다. 왜 전화를 걸 었는지 잘 모르겠습니다 join().
Collin
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.