CodeWalk

Python线程池ThreadPoolExecutor最佳实践与陷阱

作者:专业代码师 · 2026-05-30 12:55

请列举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:立即返回,但新任务不再接受

避免死锁

  1. 不要在提交到线程池的任务中等待同一个池的另一个任务
  2. 不要在回调函数中提交任务到同一个池

Python 3.9改进with ThreadPoolExecutor() as exe不再需要显式shutdown(),上下文管理器自动等待并释放。

陷阱

  1. 任务函数中修改可变全局变量需加锁
  2. 某些库函数会阻塞解释器(如time.sleep可被中断,但C扩展不一定)
  3. 默认max_workers=None(Python 3.8+为min(32, os.cpu_count() + 4)