多线程 和 多进程
理解线程、进程的区别与 Python GIL 锁的影响
掌握
threading多线程和multiprocessing多进程的基础写法掌握线程池 / 进程池的使用,能根据场景选择正确的并发方案
核心讲解
(1)核心概念
进程:操作系统分配资源的最小单位,每个进程有独立的内存空间,进程间数据隔离
线程:CPU 调度的最小单位,线程属于进程,同一个进程的线程共享内存
GIL 锁(全局解释器锁):CPython 的机制,同一时刻只有一个线程执行 Python 字节码。所以 Python 多线程无法利用多核 CPU 并行计算,适合 IO 密集型任务;计算密集型任务要用多进程。
(2)适用场景
多线程:IO 密集型任务(网络请求、文件读写、数据库操作),线程切换快、开销小
多进程:CPU 密集型任务(大量计算、图像处理、数据处理),利用多核 CPU 并行
(3)threading 多线程
创建线程:
threading.Thread(target=函数, args=参数)启动:
start()等待结束:
join()守护线程:
daemon=True,主线程结束时守护线程自动结束
(4)multiprocessing 多进程
API 和 threading 高度相似,Process 类对应 Thread,用法基本一致,但每个进程有独立内存。
(5)线程池 / 进程池(推荐)
频繁创建销毁线程 / 进程开销大,用池化技术复用,批量提交任务。
concurrent.futures.ThreadPoolExecutor线程池concurrent.futures.ProcessPoolExecutor进程池统一 API:
submit()提交任务、result()获取结果、map()批量执行
代码示例
import time
import threading
import multiprocessing
from concurrent.futures import ThreadPoolExecutor, ProcessPoolExecutor
# ===== 1. 多线程示例 =====
def task(name, seconds):
print(f"线程 {name} 开始,休眠{seconds}秒")
time.sleep(seconds)
print(f"线程 {name} 结束")
# 创建线程
t1 = threading.Thread(target=task, args=("A", 2))
t2 = threading.Thread(target=task, args=("B", 3))
start = time.time()
# 启动线程
t1.start()
t2.start()
# 等待线程结束
t1.join()
t2.join()
print(f"多线程总耗时:{time.time() - start:.2f}秒")
# 输出约3秒,说明并行执行
# ===== 2. 线程池示例(推荐) =====
def download(url):
print(f"下载:{url}")
time.sleep(1) # 模拟IO
return f"{url} 下载完成"
urls = ["url1", "url2", "url3", "url4", "url5"]
print("\n线程池执行:")
start = time.time()
with ThreadPoolExecutor(max_workers=3) as executor:
# 提交所有任务
futures = [executor.submit(download, url) for url in urls]
# 获取结果
for future in futures:
print(future.result())
print(f"线程池总耗时:{time.time() - start:.2f}秒")
# ===== 3. 多进程示例(计算密集型) =====
def cpu_task(n):
# 模拟计算密集型任务
total = 0
for i in range(n):
total += i * i
return total
if __name__ == "__main__": # Windows多进程必须加这句
print("\n多进程执行:")
start = time.time()
with ProcessPoolExecutor(max_workers=4) as executor:
results = list(executor.map(cpu_task, [10_000_000]*4))
print("计算结果:", results)
print(f"多进程总耗时:{time.time() - start:.2f}秒")
常见误区
GIL 认知错误:以为多线程能利用多核加速计算,计算密集型用多线程反而更慢
共享数据线程安全:多线程共享全局变量,同时修改会导致数据错乱,需要加锁(
threading.Lock)Windows 多进程报错:多进程代码必须放在
if __name__ == "__main__":里,否则会无限递归创建进程线程数越多越好:线程太多会导致频繁切换,反而降低性能,IO 密集型一般设为 CPU 核心数的 2~4 倍
进程间通信复杂:多进程内存隔离,不能直接共享变量,需要用队列、管道等方式通信
课后练习
用多线程实现:同时下载 3 个网页(模拟用 time.sleep),计算总耗时
用线程池批量处理 10 个任务,设置最大线程数为 3
编写一个计算密集型函数,对比单线程和多进程的耗时差异
思考:为什么爬虫程序一般用多线程而不是多进程?

