Python 多线程详解
1. 多线程的概念
在 Python 中,多线程是指在一个进程内同时运行多个线程,以便在某些场景下提升程序的响应能力或并发性。
线程是 CPU 调度的最小单位,而进程是系统资源分配的最小单位。一个进程可以包含多个线程,这些线程共享同一进程的资源(包括内存空间)。
⚠ 注意 :Python 的 GIL(Global Interpreter Lock,全局解释器锁) 限制了同一时刻只能有一个线程执行 Python 字节码 ,所以多线程在计算密集型任务中无法真正实现并行,更多用于 I/O 密集型任务(例如网络请求、文件读写)。
2. 多线程的初始化
在 Python 中可以使用 threading 模块来创建和管理线程。
2.1 使用 threading.Thread
python
import threading
import time
def worker(name):
print(f"线程 {name} 开始工作")
time.sleep(1)
print(f"线程 {name} 工作结束")
# 创建线程对象
t1 = threading.Thread(target=worker, args=("A",))
t2 = threading.Thread(target=worker, args=("B",))
# 启动线程
t1.start()
t2.start()
# 等待线程结束
t1.join()
t2.join()
print("所有线程执行完毕")
关键参数:
target:线程运行的函数。args:传给函数的参数(元组形式)。kwargs:传给函数的关键字参数。
3. 线程之间的变量传递
3.1 共享变量
线程之间可以共享同一个全局变量(因为它们在同一个进程中),但这会引发 数据竞争 问题。
python
import threading
counter = 0 # 全局变量
def increment():
global counter
for _ in range(100000):
counter += 1 # 多线程这里可能会出错
threads = []
for _ in range(5):
t = threading.Thread(target=increment)
threads.append(t)
t.start()
for t in threads:
t.join()
print("counter =", counter)
上面的代码可能不会得到想象的结果,因为多个线程同时修改 counter 会产生 竞态条件。
4. 线程锁(Lock)
为避免数据竞争,我们需要使用 锁(Lock)来确保某段代码同一时刻只有一个线程能执行。
python
import threading
counter = 0
lock = threading.Lock()
def safe_increment():
global counter
for _ in range(100000):
with lock: # 自动 acquire/release
counter += 1
threads = []
for _ in range(5):
t = threading.Thread(target=safe_increment)
threads.append(t)
t.start()
for t in threads:
t.join()
print("counter =", counter) # 结果正确
4.1 Lock 的用法
python
lock = threading.Lock()
# 方式1
lock.acquire()
try:
# 临界区代码
pass
finally:
lock.release()
# 方式2 (推荐)
with lock:
# 临界区代码
pass
5. 线程间通信
除了共享变量,还可以使用 队列(Queue) 来安全地进行线程间数据传递。
python
import threading
import queue
import time
q = queue.Queue()
def producer():
for i in range(5):
q.put(i)
print(f"生产数据 {i}")
time.sleep(0.2)
def consumer():
while True:
item = q.get()
if item is None: # 遇到 None 退出
break
print(f"消费数据 {item}")
time.sleep(0.3)
q.task_done()
t1 = threading.Thread(target=producer)
t2 = threading.Thread(target=consumer)
t1.start()
t2.start()
t1.join()
q.put(None) # 通知消费者退出
t2.join()
优点 :queue.Queue() 是线程安全的,内部已经做好了加锁处理,无需手动使用 Lock。
6. 守护线程(Daemon Thread)
守护线程会在主线程结束时自动退出。
python
t = threading.Thread(target=worker)
t.daemon = True # 设为守护线程
t.start()
适合做后台任务,例如日志记录或心跳检测。
7. 线程池(ThreadPoolExecutor)
如果需要频繁创建和销毁线程,可以使用线程池来提升性能。
python
from concurrent.futures import ThreadPoolExecutor
def task(name):
print(f"{name} 开始")
return f"{name} 完成"
with ThreadPoolExecutor(max_workers=3) as executor:
futures = [executor.submit(task, f"任务{i}") for i in range(5)]
for future in futures:
print(future.result())
8. 总结与建议
- 计算密集型任务 :推荐使用多进程(
multiprocessing)而不是多线程,避免 GIL 限制。 - I/O 密集型任务 :推荐使用多线程或异步(
asyncio)。 - 使用 Lock 或 Queue 解决数据竞争和线程安全问题。
- 合理规划线程数量,不要盲目创建过多线程。
- 可以用 ThreadPoolExecutor 简化线程管理。