通俗地讲,threading.Thread子类的join方法,是一个用来阻塞主线程的方法。就是就是在某个子线程执行到该语句时,让这个线程执行完毕,才会继续执行其他子线程,当所有调用了join方法的子线程执行结束,主线程才会结束。
而引入了参数timeout,给了每个子线程规定的阻塞时间,为join方法添加了很多用法;另外结合setDaemon()方法,在有无守护进程的场景下战线出了不同作用,以下是结合二者的分析。
1、无join,setDaemon(False)
import time
import threading
class MyThread(threading.Thread):
def run(self):
for i in range(5):
print('thread {}, @number: {}'.format(self.name, i))
time.sleep(1)
def main():
print("Start main threading")
threads = [MyThread() for i in range(3)]
for t in threads:
t.start()
print("End Main threading")
if __name__ == '__main__':
main()
该模块在main()主线程中,实例化了三个子线程的对象,其中每个子线程的任务是打印五句话,每次打印之间间隔一秒。
Start main threading
thread Thread-1, @number: 0
thread Thread-2, @number: 0
thread Thread-3, @number: 0
End Main threading
thread Thread-1, @number: 1
thread Thread-3, @number: 1
thread Thread-2, @number: 1
thread Thread-3, @number: 2
thread Thread-2, @number: 2
thread Thread-1, @number: 2
thread Thread-1, @number: 3
thread Thread-3, @number: 3
thread Thread-2, @number: 3
thread Thread-1, @number: 4
thread Thread-2, @number: 4
thread Thread-3, @number: 4
结果表明,正常情况下,并发执行的三个子线程每一次打印的行为在一秒内完成,主线程也在一秒内完成,子线程剩下的任务则在余下的四秒后完成。
2、无join,setDaemon(True)
def main():
print("Start main threading")
threads = [MyThread() for i in range(3)]
for t in threads:
t.setDaemon(True)
t.start()
print("End Main threading")
Start main threading
thread Thread-1, @number: 0
thread Thread-2, @number: 0
thread Thread-3, @number: 0
End Main threading
此时,只要主线程执行完毕,会立即结束子线程的运行。因为主线程在一秒内执行完,因此只能打印出第一轮循环的结果。
3、有join无参数,setDaemon(False)
def main():
print("Start main threading")
threads = [MyThread() for i in range(3)]
for t in threads:
t.start()
t.join()
print("End Main threading")
Start main threading
thread Thread-1, @number: 0
thread Thread-1, @number: 1
thread Thread-1, @number: 2
thread Thread-1, @number: 3
thread Thread-1, @number: 4
thread Thread-2, @number: 0
thread Thread-2, @number: 1
thread Thread-2, @number: 2
thread Thread-2, @number: 3
thread Thread-2, @number: 4
thread Thread-3, @number: 0
thread Thread-3, @number: 1
thread Thread-3, @number: 2
thread Thread-3, @number: 3
thread Thread-3, @number: 4
End Main threading
def main():
print("Start main threading")
threads = [MyThread() for i in range(3)]
for t in threads:
t.start()
for t in threads:
t.join()
print("End Main threading")
Start main threading
thread Thread-1, @number: 0
thread Thread-2, @number: 0
thread Thread-3, @number: 0
thread Thread-1, @number: 1
thread Thread-3, @number: 1
thread Thread-2, @number: 1
thread Thread-1, @number: 2
thread Thread-2, @number: 2
thread Thread-3, @number: 2
thread Thread-2, @number: 3
thread Thread-3, @number: 3
thread Thread-1, @number: 3
thread Thread-3, @number: 4
thread Thread-1, @number: 4
thread Thread-2, @number: 4
End Main threading
对比这两种情况:
第一种情况是每个子线程运行后立马调用join方法,这样会使得程序等待该子线程执行完毕后,才会执行下一个子进程,当所有的子线程执行结束后,才会结束主线程。
第二种情况是,三个子线程同时开始执行,执行后依次调用join方法,这样的运行结果和没有join方法时类似,只不过主线程会在所有子线程运行结束后才结束。
那join的作用,是否是只影响调用该方法之后的子线程呢,调用该方法之前就开始运行的子线程不受其影响吗?
def main():
print("Start main threading")
threads = [MyThread() for i in range(3)]
t1 = threads[0]
t2 = threads[1]
t3 = threads[2]
t1.start()
t2.start()
t1.join()
t2.join()
t3.start()
t3.join()
print("End Main threading")
Start main threading
thread Thread-1, @number: 0
thread Thread-2, @number: 0
thread Thread-2, @number: 1
thread Thread-1, @number: 1
thread Thread-2, @number: 2
thread Thread-1, @number: 2
thread Thread-2, @number: 3
thread Thread-1, @number: 3
thread Thread-2, @number: 4
thread Thread-1, @number: 4
thread Thread-3, @number: 0
thread Thread-3, @number: 1
thread Thread-3, @number: 2
thread Thread-3, @number: 3
thread Thread-3, @number: 4
End Main threading
结果表明,join阻塞了子线程3和主线程。子线程1和2并行执行,直到结束才开始3的执行。因此,join只影响其后的线程。
4、有join有参数,setDaemon(False)
def main():
print("Start main threading")
threads = [MyThread() for i in range(3)]
for t in threads:
t.start()
t.join(2)
print("End Main threading")
Start main threading
thread Thread-1, @number: 0
thread Thread-1, @number: 1
thread Thread-2, @number: 0
thread Thread-1, @number: 2
thread Thread-2, @number: 1
thread Thread-1, @number: 3
thread Thread-3, @number: 0
thread Thread-2, @number: 2
thread Thread-1, @number: 4
thread Thread-3, @number: 1
thread Thread-2, @number: 3
End Main threading
thread Thread-3, @number: 2
thread Thread-2, @number: 4
thread Thread-3, @number: 3
thread Thread-3, @number: 4
当设置join方法的timeout参数时,主线程会等待所有子线程各自的timeout之和,之后主线程结束,子线程继续执行。(在End Main threading之前,执行了六秒)
def main():
print("Start main threading")
threads = [MyThread() for i in range(3)]
for t in threads:
t.start()
for t in threads:
t.join(1)
print("End Main threading")
Start main threading
thread Thread-1, @number: 0
thread Thread-2, @number: 0
thread Thread-3, @number: 0
thread Thread-3, @number: 1
thread Thread-1, @number: 1
thread Thread-2, @number: 1
thread Thread-3, @number: 2
thread Thread-2, @number: 2
thread Thread-1, @number: 2
End Main threading
thread Thread-1, @number: 3
thread Thread-3, @number: 3
thread Thread-2, @number: 3
thread Thread-1, @number: 4
thread Thread-3, @number: 4
thread Thread-2, @number: 4
即便是在所有子线程开始后,也同样等待六秒,主线程结束,其余子线程继续执行。区别在于子线程的执行顺序,这个顺序和join无参数的情况是一致的。
5、有join无参数,setDaemon(True)
def main():
print("Start main threading")
threads = [MyThread() for i in range(3)]
for t in threads:
t.setDaemon(True)
t.start()
for t in threads:
t.join()
print("End Main threading")
Start main threading
thread Thread-1, @number: 0
thread Thread-2, @number: 0
thread Thread-3, @number: 0
thread Thread-1, @number: 1
thread Thread-2, @number: 1
thread Thread-3, @number: 1
thread Thread-1, @number: 2
thread Thread-2, @number: 2
thread Thread-3, @number: 2
thread Thread-2, @number: 3
thread Thread-3, @number: 3
thread Thread-1, @number: 3
thread Thread-2, @number: 4
thread Thread-3, @number: 4
thread Thread-1, @number: 4
End Main threading
守护线程只作用于主线程,上述代码的主线程是当所有子线程执行完毕后执行完,因此从结果来看,并无差异。
同样,在每个子线程开始后立马执行无参数join语句,因为主线程本身就是最后结束,因此守护线程情况下,效果也无差异。(省略代码以及结果)
6、有join有参数,setDaemon(True)
def main():
print("Start main threading")
threads = [MyThread() for i in range(3)]
for t in threads:
t.setDaemon(True)
t.start()
t.join(1)
print("End Main threading")
Start main threading
thread Thread-1, @number: 0
thread Thread-1, @number: 1
thread Thread-2, @number: 0
thread Thread-1, @number: 2
thread Thread-2, @number: 1
thread Thread-3, @number: 0
thread Thread-1, @number: 3
End Main threading
thread Thread-3, @number: 1
每个子线程开始后立即执行join一秒的语句,当他们的时间之和结束后,主线程结束,线程没有执行完毕的部分被强制杀死。另一种情况也是如此。
这里需要注意的是,End语句打印后又打印了一句,而另一种join位置也是如此,我分析,这是因为执行print这句话的时候,已经是第四秒开始,主线程结束这句话所消耗的时间,子线程会继续执行,直到真正终止的时刻。因此会有一些超过三秒的打印语句被打印的情况。
总结
join的主要目的就是阻塞主线程
join无参数时,join阻塞了其语句之后才开始的子线程和主线程
join有参数时:
- 守护线程时,主线程等待所有子线程join的timeout时间之和,之后杀死所有子线程,退出程序。
- 非守护线程时,主线程等待所有子线程timeout时间之和,之后主线程结束,子线程继续执行直到结束。