263

我正在研究 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()因为它不是守护进程,即使我删除它我也看不到任何变化

4

10 回答 10

355

一个有点笨拙的 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

所以你看不到任何变化的原因是你的主线程在你的join. 您可以说join(仅)与主线程的执行流程相关。

例如,如果您想同时下载一堆页面以将它们连接成一个大页面,您可以使用线程开始并发下载,但需要等到最后一个页面/线程完成才能开始组装单个页面在许多中。那是你使用的时候join()

于 2013-02-26T10:00:20.560 回答
85

直接来自文档

join([timeout]) 等到线程终止。这会阻塞调用线程,直到调用其 join() 方法的线程终止(正常或通过未处理的异常)或直到发生可选超时。

这意味着产生t和的主线程d等待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完成。

于 2013-02-26T09:31:12.417 回答
51

感谢这个帖子——它也帮助了我很多。

我今天学到了一些关于 .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() 视为主线程上的“保留”的方法——它在主线程可以继续之前取消线程并在主线程中顺序执行。它确保您的线程在主线程向前移动之前完成。请注意,这意味着如果您的线程在调用 join() 之前已经完成,则可以 - 调用 join() 时会立即释放主线程。

事实上,我现在才想到,主线程在 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"

它产生这个输出(注意打印语句是如何相互串接的。)

$ 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() 完成之前完成,主线程继续执行打印,然后 t2.join() 然后打印,然后 t3.join() 然后打印。

欢迎指正。我也是线程新手。

(注意:如果您有兴趣,我正在为 DrinkBot 编写代码,我需要线程来同时运行配料泵,而不是按顺序运行——等待每杯饮料的时间更少。)

于 2016-05-20T04:25:55.540 回答
21

方法join()

阻塞调用线程,直到调用 join() 方法的线程终止。

来源:http ://docs.python.org/2/library/threading.html

于 2013-02-26T09:30:59.273 回答
11

加入 - 解释器将等到您的进程完成终止

>>> 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
于 2019-03-06T05:04:03.757 回答
3

这个例子演示了这个.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 is killed, this thread will be killed as well. 
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
于 2019-01-22T09:41:50.540 回答
3

在 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! 

'''
于 2019-02-25T17:50:03.763 回答
1

join(t)为非守护线程和守护线程创建函数时,主线程(或主进程)应该等待t几秒钟,然后才能继续在自己的进程上工作。在t秒的等待时间内,两个子线程都应该做他们能做的事情,比如打印一些文本。几秒钟后t,如果非守护线程仍然没有完成它的工作,并且在主进程完成它的工作后它仍然可以完成它,但是对于守护线程,它只是错过了它的机会窗口。但是,它最终会在python程序退出后死掉。如果有什么问题,请纠正我。

于 2013-09-09T14:39:47.313 回答
0

主线程(或任何其他线程)加入其他线程有几个原因

  1. 一个线程可能已经创建或持有(锁定)一些资源。加入调用线程可能能够代表它清除资源

  2. join() 是一个自然阻塞调用,用于在被调用线程终止后继续调用连接调用线程。

如果一个 python 程序没有加入其他线程,python 解释器仍然会代表它加入非守护线程。

于 2020-01-16T08:04:56.263 回答
-3

“使用 join() 有什么用?” 你说。真的,这与“关闭文件有什么用,因为当我的程序退出时python和操作系统会为我关闭我的文件?”的答案相同。

这只是一个好的编程问题。您应该在代码中不再运行线程的位置加入()您的线程因为您必须确保线程没有运行以干扰您自己的代码,或者您希望在更大的系统。

您可能会说“我不希望我的代码延迟给出答案”只是因为 join() 可能需要额外的时间。这在某些情况下可能是完全有效的,但您现在需要考虑到您的代码“为 python 和操作系统清理留下了杂乱无章的东西”。如果您出于性能原因这样做,我强烈建议您记录该行为。如果您正在构建期望其他人使用的库/包,则尤其如此。

除了性能原因之外,没有理由不加入(),我认为您的代码不需要表现得那么好。

于 2013-12-19T23:05:19.807 回答