最常用且推荐的方式是调用子线程对象的 join() 方法;必须在 start() 后调用,可设超时,多线程时遍历调用,守护线程需谨慎使用,高级场景推荐 ThreadPoolExecutor。

主线程等待子线程结束,最常用且推荐的方式是调用子线程对象的 join() 方法。
使用 join() 等待单个子线程
在启动子线程后,直接在其上调用 join(),主线程会阻塞,直到该子线程执行完毕。
- 必须在
start()之后调用join(),否则无效果或抛出异常 - 可传入超时参数(如
join(5)),避免无限等待 - 若子线程已结束,
join()会立即返回
等待多个子线程完成
当有多个子线程时,对每个线程依次调用 join() 即可确保全部结束。
- 顺序调用不会影响线程实际执行顺序,只控制主线程的等待逻辑
- 建议将线程对象保存在列表中,便于统一管理与遍历等待
- 例如:
for t in threads: t.join()
注意 daemon 线程的特殊情况
如果子线程设置为守护线程(daemon=True),主线程退出时会强制终止它,join() 仍可等待,但程序整体行为需谨慎设计。
- 守护线程适合后台服务类任务(如日志轮转、心跳上报)
- 涉及资源清理或关键逻辑的线程,应设为非守护(默认),并配合
join() - 不要依赖守护线程完成必须的收尾工作
替代方案:使用 concurrent.futures
对于更高级的任务编排,推荐使用 ThreadPoolExecutor,其 submit() + as_completed() 或 wait() 更易管理。
-
executor.shutdown(wait=True)默认会等待所有任务完成 - 返回的
Future对象支持result()阻塞获取结果,隐式等待 - 代码更简洁,异常传播和资源管理也更健壮

















