我在学习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()的用途是什么,因为它不是守护进程,即使我删除它,我也看不到任何变化


当前回答

Join()同时等待非守护进程和守护进程线程完成。 如果没有join(),将运行非守护进程线程,并与主线程并发完成。 如果没有join(),守护线程将与主线程并发运行,当主线程完成时,如果守护线程仍在运行,守护线程将在未完成的情况下退出。

因此,下面的join()和daemon=False(守护线程)(daemon默认为False):

import time
from threading import Thread

def test1():
    for _ in range(3):
        print("Test1 is running...")
        time.sleep(1)
    print("Test1 is completed")
    
def test2():
    for _ in range(3):
        print("Test2 is running...")
        time.sleep(1)
    print("Test2 is completed")
                               # Here
thread1 = Thread(target=test1, daemon=False)
thread2 = Thread(target=test2, daemon=False)
                               # Here
thread1.start()
thread2.start()
thread1.join() # Here
thread2.join() # Here
print("Main is completed")

或者,使用join()和daemon=True(非守护线程):

# ...
                               # Here
thread1 = Thread(target=test1, daemon=True)
thread2 = Thread(target=test2, daemon=True)
                               # Here
# ...
thread1.join() # Here
thread2.join() # Here
print("Main is completed")

join()等待Test1和Test2非守护进程或守护进程线程完成。因此,Main is completed在Test1和Test2线程完成后打印,如下所示:

Test1 is running...
Test2 is running...
Test1 is running...
Test2 is running...
Test1 is running...
Test2 is running...
Test1 is completed
Test2 is completed
Main is completed

并且,如果不使用join(),如果daemon=False(非守护线程):

# ...
                               # Here
thread1 = Thread(target=test1, daemon=False)
thread2 = Thread(target=test2, daemon=False)
                               # Here
# ...
# thread1.join()
# thread2.join()
print("Main is completed")

Test1和Test2非守护线程正在与主线程并发地运行和完成。因此,Main is completed在Test1和Test2线程完成之前打印,如下所示:

Test1 is running...
Test2 is running...
Main is completed
Test1 is running...
Test2 is running...
Test1 is running...
Test2 is running...
Test1 is completed
Test2 is completed

并且,如果没有使用join(),如果daemon=True(守护线程):

# ...
                               # Here
thread1 = Thread(target=test1, daemon=True)
thread2 = Thread(target=test2, daemon=True)
                               # Here
# ...
# thread1.join()
# thread2.join()
print("Main is completed")

Test1和Test2守护线程与主线程并发运行。因此,Main is completed在Test1和Test2守护线程完成之前打印,当主线程完成时,Test1和Test2守护线程将在未完成的情况下退出,如下所示:

Test1 is running...
Test2 is running...
Main is completed

其他回答

Join()同时等待非守护进程和守护进程线程完成。 如果没有join(),将运行非守护进程线程,并与主线程并发完成。 如果没有join(),守护线程将与主线程并发运行,当主线程完成时,如果守护线程仍在运行,守护线程将在未完成的情况下退出。

因此,下面的join()和daemon=False(守护线程)(daemon默认为False):

import time
from threading import Thread

def test1():
    for _ in range(3):
        print("Test1 is running...")
        time.sleep(1)
    print("Test1 is completed")
    
def test2():
    for _ in range(3):
        print("Test2 is running...")
        time.sleep(1)
    print("Test2 is completed")
                               # Here
thread1 = Thread(target=test1, daemon=False)
thread2 = Thread(target=test2, daemon=False)
                               # Here
thread1.start()
thread2.start()
thread1.join() # Here
thread2.join() # Here
print("Main is completed")

或者,使用join()和daemon=True(非守护线程):

# ...
                               # Here
thread1 = Thread(target=test1, daemon=True)
thread2 = Thread(target=test2, daemon=True)
                               # Here
# ...
thread1.join() # Here
thread2.join() # Here
print("Main is completed")

join()等待Test1和Test2非守护进程或守护进程线程完成。因此,Main is completed在Test1和Test2线程完成后打印,如下所示:

Test1 is running...
Test2 is running...
Test1 is running...
Test2 is running...
Test1 is running...
Test2 is running...
Test1 is completed
Test2 is completed
Main is completed

并且,如果不使用join(),如果daemon=False(非守护线程):

# ...
                               # Here
thread1 = Thread(target=test1, daemon=False)
thread2 = Thread(target=test2, daemon=False)
                               # Here
# ...
# thread1.join()
# thread2.join()
print("Main is completed")

Test1和Test2非守护线程正在与主线程并发地运行和完成。因此,Main is completed在Test1和Test2线程完成之前打印,如下所示:

Test1 is running...
Test2 is running...
Main is completed
Test1 is running...
Test2 is running...
Test1 is running...
Test2 is running...
Test1 is completed
Test2 is completed

并且,如果没有使用join(),如果daemon=True(守护线程):

# ...
                               # Here
thread1 = Thread(target=test1, daemon=True)
thread2 = Thread(target=test2, daemon=True)
                               # Here
# ...
# thread1.join()
# thread2.join()
print("Main is completed")

Test1和Test2守护线程与主线程并发运行。因此,Main is completed在Test1和Test2守护线程完成之前打印,当主线程完成时,Test1和Test2守护线程将在未完成的情况下退出,如下所示:

Test1 is running...
Test2 is running...
Main is completed

When making join(t) function for both non-daemon thread and daemon thread, the main thread (or main process) should wait t seconds, then can go further to work on its own process. During the t seconds waiting time, both of the children threads should do what they can do, such as printing out some text. After the t seconds, if non-daemon thread still didn't finish its job, and it still can finish it after the main process finishes its job, but for daemon thread, it just missed its opportunity window. However, it will eventually die after the python program exits. Please correct me if there is something wrong.

一个有点笨拙的ascii-art来演示机制: 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()方法的线程终止—正常终止或通过未处理的异常终止—或者直到可选超时发生。

这意味着生成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完成。

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

线程可能已经创建或持有(锁定)一些资源。调用连接的线程可以代表它清除资源 Join()是一个自然的阻塞调用,用于调用连接的线程在被调用的线程终止后继续执行。

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