因为中文网络上充斥着各种关于多线程, 多进程的流言, 这次来当一次流言终结者 (请务必看完”结论”和”注意点”).
结论
- 多线程间的切换由 OS 调度, 不可控
- 主线程默认不会等待子线程结束, 主线程执行完毕立即结束 (join 方法让主线程等待子线程执行完毕再结束)
- 主线程结束后, 子线程不会退出, 直到子线程执行完毕后自行退出 (setDemon 方法让主线程结束后立即杀死子线程)
证明
from threading import Threadfrom time import sleepdef func_test():for i in range(10):print('child {}'.format(i))sleep(1)def main():t = Thread(target=func_test)# t.daemon = Truet.start()for i in range(5):print('father {}'.format(i))sleep(1)# t.join()error = 1/0 # 这里确保出错让主线程退出print('father alive!') # father alive! 不会被打印, 证明主线程已退出main()
输出结果:
child 0father 0father 1child 1father 2child 2father 3child 3father 4child 4Traceback (most recent call last):File "D:/Data/code/test.py", line 319, in <module>main()File "D:/Data/code/test.py", line 316, in mainerror = 1/0 # 这里确保出错让主线程退出ZeroDivisionError: division by zerochild 5child 6child 7child 8child 9
可以很清楚的看到主线程遇到错误退出后, 子线程仍保持运行, 直到子线程也执行完毕.
注意点
- t.strat() 后子线程开始执行, t.start() 在哪, 子线程就从哪开始执行.
- t.join() 方法的位置很重要, 表示主线程在哪里等待子线程, 如果 t.start() 后立即 t.join() 则主线程被阻塞在原地等待子线程执行完毕, 相当于还是单线程, 所以 t.join() 应该放在主线程的最后位置.
- t.join(5) 表示主线程在此处会等待子线程 5 秒, 注意不需要任何前提条件都可以使用(不需要设置守护线程).
- t.daemon = True 或 t.setDaemon(True) 是将子线程设置为守护线程, 什么是守护线程, 就像是打倒 BOSS 后所有小兵原地自爆, 此选项使主线程结束后立即杀死子线程. 因为原本主线程就不会等待子线程, 主线程结束后, 子线程会变成”孤儿”继续运行, 直到结束, 这是默认情况 (孤儿线程可能会引起各种各样的问题, 应尽力避免).
- 经证实, Process 在这些特性上与 Thread 相同.
拓展
多线程中如何获取子线程的返回值 ?
class MyThread(Thread):def __init__(self, func, args=()):super(MyThread, self).__init__()self.func = funcself.args = argsdef run(self):self.result = self.func(*self.args)def get_result(self):return self.result
调用 t.get_result() 方法获取返回值.
