Python 的多线程在处理
I/O
密集型任务时非常有用。下面我会系统地讲解 Python 多线程地核心知识点,并重点深入解释你提到地几个关键问题(守护线程、join()
等)
一、Python 多线程基础
Python 中的多线程主要通过 threading
模块实现。每个线程都是 threading.Thread
类的一个实例。
1.1 创建线程的两种方式
# 方法一:传入函数
import threading
def task():
print("子线程执行任务")
t = threading.Thread(target=task)
t.start()
# 方法二:继承Thread类
class MyThread(threading.Thread):
def run(self):
print("子线程执行任务")
t = MyThread()
t.start()
二、Thread类中的核心参数详解
threading.Thread(
group=None,
target=None,
name=None,
args=(),
kwargs={},
daemon=None
)
✅ 关键参数解释:
参数名 | 说明 |
---|---|
target | 要执行的函数对象 |
args | 给目标函数传的位置参数(元组) |
kwargs | 给目标函数传的关键字参数(字典) |
name | 线程名称,默认自动生成 |
daemon | 是否是“守护线程”(重点解释如下) |
三、什么是守护线程(daemon)
3.1 基本理解:
守护线程是 依附于主线程 的线程。如果主线程(程序入口)结束了,那么 所有还在运行的守护线程都会被强制终 止。
🌟 类比理解:
可以把主线程看作 ”主进程“,守护线程就是 ”打杂的小弟“。主线程死了,小弟自动也跟着被干掉,连收拾残局的机会都没有。
🧪 示例:
import threading
import time
def worker():
while True:
print("子线程运行中...")
time.sleep(1)
t = threading.Thread(target=worker)
t.daemon = True # 设置为守护线程
t.start()
time.sleep(3)
print("主线程结束")
输出结果:
子线程运行中...
子线程运行中...
子线程运行中...
主线程结束
(程序退出,守护线程被强制关闭)
❗ 注意:
- 一定要在
start()
之前设置daemon = True
,否则会报错。 - 守护线程不会执行任何 ”清理“ 或 ”善后“ 代码(如
finally
块)。
⛓️ 四、线程的 join()
4.1 基本定义:
join()
的作用是:让主线程等待子线程执行完成后再继续执行。
👀 谁对谁可见?
是 主线程调用子线程的 join(),意思是让主线程 ”卡住等你执行完“。
🌟 示例说明:
import threading
import time
def worker():
print("子线程开始")
time.sleep(2)
print("子线程结束")
t = threading.Thread(target=worker)
t.start()
print("主线程等待子线程")
t.join() # 等待子线程 执行完成
print("子线程执行完了,主线程继续")
输出:
子线程开始
主线程等待子线程
子线程结束
子线程执行完了,主线程继续
💥 五、join() 后子线程还存在吗?会立即销毁吗?
这个问题非常好,也是很多人搞混的点。
✅ 正确理解:
- 调用
join()
并不是销毁线程,而是 ”等这个线程运行完“。 - 线程结束后,线程对象依然存在,只是状态变成了 ”终止“ (
t.is_alive() == False
)。 - Python 会自动做资源清理,不需要我们手动销毁线程对象。
🔍 验证示例:
import threading
import time
def worker():
print("子线程执行")
time.sleep(1)
t = threading.Thread(target=worker)
t.start()
t.join()
print("子线程还活着吗?", t.is_alive())
输出:
子线程执行
子线程还活着吗? False
所以你看到的是,线程运行完后,确实不再“活着”了,但它的对象还在,只是状态改变了。
🔁 六、守护线程 vs 非守护线程 vs join() 的配合理解
场景 | 效果 |
---|---|
非守护线程 + 不 join | 主线程执行完后,会等子线程执行完才退出 |
守护线程 + 不 join | 主线程结束后,子线程立即被强制终止 |
非守护线程 + join | 主线程会等待子线程执行完,确保同步 |
守护线程 + join | 正常等待,join 优先级高,主线程会等子线程执行完(只要主线程不先结束) |
🧩 七、结语和建议
如果你刚开始学多线程,可以先牢牢记住以下几点:
daemon=True
是“我不重要,主线程结束我就结束”。join()
是“你别走,等我干完这件事 再走”。- 一般 I/O 操作(如爬虫、网络请求)适合用多线程来加速。
- 如果有资源共享需求,一定注意线程同步(如使用
Lock
)。