❝Python入门第三十八课,主要介绍在实际开发中,面对具体开发场景时,如何选择多进程还是多线程。
选择原则
多进程与多线程的选择核心取决于任务类型:I/O密集型任务优先使用多线程,CPU密集型任务优先使用多进程。
这一原则源于两者在资源隔离性、通信成本和调度开销上的本质差异。下文从不同关键维度进行展开分析。
本质区别:为什么选择逻辑不同?
1、资源与隔离性
- 多进程:每个进程拥有独立内存空间和资源系统,进程间严格隔离。一个进程崩溃不会影响其他进程,但资源占用高、通信需通过IPC(如管道、内存共享),开销较大。
- 多线程:线程共享所属进程的内存和资源,通信直接通过共享变量,无需序列化。但一个线程崩溃可能导致整个进程终止,隔离性较弱。
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进程)。
原因:进程隔离性强,单个进程崩溃不影响全局,适合容错性要求高的系统。
案例演示:CPU密集型任务,更适合用多进程
import timefrom concurrent.futures import ProcessPoolExecutor, ThreadPoolExecutor# CPU 密集型任务defcpu_task(n): print(f'任务{n}开始了') total = 0for i in range(10_000_000): total += i * ireturn totalif __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密集型任务多进程确实比多线程效率高。
案例演示:IO密集型任务,更适合用多线程
import timefrom concurrent.futures import ThreadPoolExecutor, ProcessPoolExecutor# 拷贝文件defcopy_file(index):with open('./media/demo.mp4', 'rb') as source, open(f'./media/demo_副本{index}.mp4', 'wb') as target:whileTrue: data = source.read(1024 * 1024) # 每次读 1MBifnot 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密集型任务多进程和多线程效率相差不大,但是多进程开销大。
最终建议
没有绝对最优方案,需要根据任务特性、资源约束和语言环境综合权衡。对关键系统,务必通过实际测试验证选择。