join是用于阻塞该行代码所在的线程,让.join()前面线程优先执行的函数,.join()前面线程结束后,该行代码才会继续执行下去,所以join相当于一个线程发出的指令,告诉当前行所在线程,暂停,让我先执行。

两个线程相当于两个人,在不同跑道上,这个跑道需要4步才能跑完,但是两个人的速度不一样,线程1一步需要1s,线程2一步需要0.5s,不加join执行结果如下:

import threading
import time


def test():

    for i in range(5):
        print(threading.current_thread().name, '跑了', i, '步')
        time.sleep(1)


def test2():

    for i in range(5):
        print(threading.current_thread().name, '跑了', i, '步')
        time.sleep(0.5)


thread1 = threading.Thread(target=test, name='子线程1')
thread2 = threading.Thread(target=test2, name='子线程2')
thread1.start()
thread2.start()

start()相当于起跑,test和test2同时执行,执行速度上,线程1速度比线程2慢,最后还是线程2先跑完4步,然后线程1才跑完

子线程1 跑了 0 步
子线程2 跑了 0 步
子线程2 跑了 1 步
子线程1 跑了 1 步
子线程2 跑了 2 步
子线程2 跑了 3 步
子线程1 跑了 2 步
子线程2 跑了 4 步		# 线程2先跑完4步
子线程1 跑了 3 步
子线程1 跑了 4 步

另一场比赛,两个线程同时起跑时,线程1突然拦在线程2的赛道(test2)上,不讲武德的拿出枪(join),说,你再跑一个试试
线程2屈服了,于是站在原地,等线程1跑完4步后,再开始跑

import threading
import time


def test():

    for i in range(5):
        print(threading.current_thread().name, '跑了', i, '步')
        time.sleep(1)


def test2():
    print('子线程2:我准备跑')
    thread1.join()       # 拦住线程2
    print('子线程2:我继续跑')
    for i in range(5):
        print(threading.current_thread().name, '跑了', i, '步')
        time.sleep(0.5)


thread1 = threading.Thread(target=test, name='子线程1')
thread2 = threading.Thread(target=test2, name='子线程2')
thread1.start()
thread2.start()

可以看到,线程2 准备跑的时候,突然停住了,让线程1跑完,才敢继续

子线程1 跑了 0 步
子线程2:我准备跑
子线程1 跑了 1 步
子线程1 跑了 2 步
子线程1 跑了 3 步
子线程1 跑了 4 步
子线程2:我继续跑
子线程2 跑了 0 步
子线程2 跑了 1 步
子线程2 跑了 2 步
子线程2 跑了 3 步
子线程2 跑了 4 步

由于观众不满这种毫无观赏性的比赛,于是线程1不得不想出其他方式,比如跟线程2商量,让我先跑2步(join(2)),然后你再跑

def test2():
    print('子线程2:我准备跑')
    thread1.join(2)       # 拦住线程2,让它等待2s
    print('子线程2:我继续跑')
    for i in range(5):
        print(threading.current_thread().name, '跑了', i, '步')
        time.sleep(0.5)

尽管线程2等了2s再跑,最后两个人还是几乎同时到达终点,

子线程1 跑了 0 步
子线程2:我准备跑
子线程1 跑了 1 步
子线程2:我继续跑
子线程2 跑了 0 步
子线程1 跑了 2 步
子线程2 跑了 1 步
子线程1 跑了 3 步
子线程2 跑了 2 步
子线程2 跑了 3 步
子线程1 跑了 4 步
子线程2 跑了 4 步

可以把主线程当做test2()函数中的执行部分,效果也是一样的,主线程会等待发出join指令的那个线程先执行。

守护线程

主线程执行完了,子线程不管有没有执行完,都要结束。

主线程听了两个人跑步的故事,也想来参与下,于是就线程1陪主线程玩

import threading
import time


def test():

    for i in range(5):
        print(threading.current_thread().name, '跑了', i, '步')
        time.sleep(1)


thread1 = threading.Thread(target=test, name='子线程1')

thread1.start()

for i in range(5):
    print(threading.current_thread().name, '跑了', i, '步')
    time.sleep(0.5)
子线程1 跑了 0 步
MainThread 跑了 0 步
MainThread 跑了 1 步
子线程1 跑了 1 步
MainThread 跑了 2 步
MainThread 跑了 3 步
子线程1 跑了 2 步
MainThread 跑了 4 步
子线程1 跑了 3 步
子线程1 跑了 4 步

跑了几次下来主线程发现线程1总是那么慢,每次跑完4步了线程1才跑2步,于是跟线程1说,剩下两步也别跑了,毁灭吧(setDaemon(True)),他累了

import threading
import time


def test():

    for i in range(5):
        print(threading.current_thread().name, '跑了', i, '步')
        time.sleep(1)


thread1 = threading.Thread(target=test, name='子线程1')
thread1.setDaemon(True)     # 启动前,先设置守护线程
thread1.start()

for i in range(5):
    print(threading.current_thread().name, '跑了', i, '步')
    time.sleep(0.5)

后面每次陪跑,线程1都只跑了一半,还有2步没跑,就结束了

子线程1 跑了 0 步
MainThread 跑了 0 步
MainThread 跑了 1 步
子线程1 跑了 1 步
MainThread 跑了 2 步
MainThread 跑了 3 步
子线程1 跑了 2 步
MainThread 跑了 4 步

终于有一天,线程1无法再忍受这种生活,拿出了枪(join),说,让我先跑

import threading
import time


def test():

    for i in range(5):
        print(threading.current_thread().name, '跑了', i, '步')
        time.sleep(1)


thread1 = threading.Thread(target=test, name='子线程1')
thread1.setDaemon(True)     # 设置守护线程
thread1.start()

print('主线程:我开始跑')
thread1.join()		# 拦住主线程
print('主线程:我继续跑')

for i in range(5):
    print(threading.current_thread().name, '跑了', i, '步')
    time.sleep(0.5)

主线程对拿枪的线程1并没有什么好办法,只能让他先跑

子线程1 跑了 0 步
主线程:我开始跑
子线程1 跑了 1 步
子线程1 跑了 2 步
子线程1 跑了 3 步
子线程1 跑了 4 步
主线程:我继续跑
MainThread 跑了 0 步
MainThread 跑了 1 步
MainThread 跑了 2 步
MainThread 跑了 3 步
MainThread 跑了 4 步

join所完成的工作就是线程同步,即主线程任务结束之后,进入阻塞状态,一直等待其他的子线程执行结束之后,主线程再终止