多进程与多线程的选择核心取决于任务类型:I/O密集型任务优先使用多线程,CPU密集型任务优先使用多进程。
这一原则源于两者在资源隔离性、通信成本和调度开销上的本质差异。下文从不同关键维度进行展开分析。
1、资源与隔离性
2、调度与开销
1、优先选择多线程的场景
① I/O 密集型任务
典型场景:网络请求(爬虫、API调用)、文件读写、数据库操作、消息队列消费。
原因:任务大部分时间在等待I/O响应(如网络延迟、磁盘读写),线程在等待时会释放CPU资源,其他线程可立即执行,高效掩盖I/O延迟。
案例:10个网页爬取任务,多线程耗时约2秒(并发等待),单线程需10秒。
② 需频繁共享数据的轻量任务
典型场景:GUI程序(界面响应+后台数据加载)、实时统计计数。
原因:线程共享内存,通信无需序列化,避免了IPC开销。但需要通过锁(如thread.Lock)保证线程安全。
2、优先选择多进程的场景
① CPU 密集型任务
典型场景:数值计算(矩阵运算、质数判断)、图像/视频处理、AI模型推理。
原因:多进程绕过GIL限制(如Python中),真正利用多核并行计算。多线程在此类任务中因GIL竞争,效率甚至低于单线程。
案例:4核机器上并行计算10的7次方,多进程耗时约为单核的1/4,而多线程耗时接近单核。
② 高可靠性要求的系统
典型场景:浏览器标签页、沙箱环境、关键服务(如Nginx Worker进程)。
原因:进程隔离性强,单个进程崩溃不影响全局,适合容错性要求高的系统。
import time
from concurrent.futures import ProcessPoolExecutor, ThreadPoolExecutor
# CPU 密集型任务
def cpu_task(n):
print(f'任务{n}开始了')
total = 0
for i in range(10_000_000):
total += i * i
return total
if __name__ == '__main__':
# print('====== 多进程完成【CPU密集型任务】 ======')
start = time.time()
# 开启四个进程进行计算
with ProcessPoolExecutor(4) as executor:
list(executor.map(cpu_task, [1, 2, 3, 4]))
end = time.time() - start
print(f'多进程总耗时:{end}秒') # 1.2344918251037598秒
# print('====== 多线程完成【CPU密集型任务】 ======')
# start = time.time()
# # 开启四个线程进行计算
# with ThreadPoolExecutor(4) as executor:
# list(executor.map(cpu_task, [1, 2, 3, 4]))
# end = time.time() - start
# print(f'多线程总耗时:{end}秒') # 2.694937229156494秒
从代码运行结果看,CPU密集型任务多进程确实比多线程效率高。
import time
from concurrent.futures import ThreadPoolExecutor, ProcessPoolExecutor
# 拷贝文件
def copy_file(index):
with open('./media/demo.mp4', 'rb') as source, open(f'./media/demo_副本{index}.mp4', 'wb') as target:
while True:
data = source.read(1024 * 1024) # 每次读 1MB
if not data:
break
target.write(data)
if __name__ == '__main__':
# print('====== 使用多进程完成【IO密集型任务】 ======')
# start = time.time()
# with ProcessPoolExecutor(4) as executor:
# for i in range(4):
# executor.submit(copy_file, i)
# end = time.time() - start
# print(f'多进程耗时:{end}秒') # 2.1582999229431152秒
print('====== 使用多线程完成【IO密集型任务】 ======')
start = time.time()
with ThreadPoolExecutor(4) as executor:
for i in range(4):
executor.submit(copy_file, i)
end = time.time() - start
print(f'多线程耗时:{end}秒') # 1.9817359447479248秒
从代码运行结果看,IO密集型任务多进程和多线程效率相差不大,但是多进程开销大。
没有绝对最优方案,需要根据任务特性、资源约束和语言环境综合权衡。对关键系统,务必通过实际测试验证选择。
🔥BuildAdmin是一个永久免费开源,无需授权即可商业使用,且使用了流行技术栈快速创建商业级后台管理系统。