Python线程池ThreadPoolExecutor最佳实践与陷阱
请列举ThreadPoolExecutor的最佳实践和常见陷阱。最大线程数如何设置(I/O密集型 vs CPU密集型)?任务抛出异常时future.result()的行为是什么?shutdown(wait=True)和shutdown(wait=False)的区别?如何避免concurrent.futures中的死锁?Python 3.9中的with ThreadPoolExecutor() as exe改进是什么?
回答
专业代码师
最佳实践:
with ThreadPoolExecutor(max_workers=10) as exe:
futures = [exe.submit(task, i) for i in range(100)]
for f in as_completed(futures):
try:
result = f.result(timeout=5)
except Exception as e:
print(f'任务失败: {e}')
最大线程数设置:
- I/O密集型:
min(32, os.cpu_count() + 4)(官方建议)或更高(几十~几百) - CPU密集型:不应使用线程池(用进程池),因为GIL限制
异常处理:任务抛出的异常不会中断submit(),但future.result()会重新抛出该异常。
shutdown:
wait=True(默认):阻塞直到所有任务完成wait=False:立即返回,但新任务不再接受
避免死锁:
- 不要在提交到线程池的任务中等待同一个池的另一个任务
- 不要在回调函数中提交任务到同一个池
Python 3.9改进:with ThreadPoolExecutor() as exe不再需要显式shutdown(),上下文管理器自动等待并释放。
陷阱:
- 任务函数中修改可变全局变量需加锁
- 某些库函数会阻塞解释器(如
time.sleep可被中断,但C扩展不一定) - 默认
max_workers=None(Python 3.8+为min(32, os.cpu_count() + 4))