flask默认不支持多线程io并发,需显式启用threaded=true才可处理并发请求;生产环境应使用gunicorn或uwsgi替代开发服务器,并配合线程池与进程池合理分工——io密集型用threadpoolexecutor,cpu密集型用processpoolexecutor。

Flask 默认不支持多线程 IO 并发,别直接开 threading.Thread
Flask 自带的开发服务器(app.run())默认是单线程同步模型。即使你手动启动多个 threading.Thread 去调用 requests.get() 或读文件,主线程仍会阻塞在 WSGI 请求处理上——除非你明确启用多线程模式,且确保底层 WSGI 服务器支持。
常见错误现象:threading.Thread 启动后,HTTP 响应迟迟不返回,或并发数一高就卡死、报 RuntimeError: working outside of application context。
- 必须加
threaded=True参数启动开发服务器:app.run(threaded=True)(仅限开发环境) - 生产环境务必换用
gunicorn或uwsgi,并配置多 worker + 多线程(如gunicorn --threads 4 --workers 2) - 每个线程需手动激活 Flask 上下文:
with app.app_context():或with app.request_context({}):,否则无法访问current_app、g等
concurrent.futures.ThreadPoolExecutor 是更安全的线程管理方式
相比裸写 threading.Thread,ThreadPoolExecutor 自动管理线程生命周期、异常传播和资源回收,更适合处理短时 IO 任务(如调用第三方 API、读取本地 JSON 文件)。
使用场景:需要同时发起 3–10 个 HTTP 请求并聚合结果,且不能让请求等待超过 5 秒。
- 定义全局 executor(避免每次请求都新建):
executor = ThreadPoolExecutor(max_workers=4) - 提交任务用
executor.submit(func, *args),返回Future对象 - 务必设超时:
future.result(timeout=5),否则线程池可能被挂起 - 不要在
submit的函数里直接操作 Flask 的request或session——它们不是线程安全的,需提前提取必要参数传入
示例片段:
from concurrent.futures import ThreadPoolExecutor
import requests
executor = ThreadPoolExecutor(max_workers=3)
def fetch_url(url):
return requests.get(url, timeout=3).json()
@app.route('/aggregate')
def aggregate():
urls = ['https://httpbin.org/delay/1', 'https://httpbin.org/delay/2']
futures = [executor.submit(fetch_url, u) for u in urls]
results = []
for f in futures:
try:
results.append(f.result(timeout=4))
except Exception as e:
results.append({'error': str(e)})
return {'results': results}
异步 IO(asyncio + aiohttp)比多线程更适合高并发网络请求
当你要并发处理几十甚至上百个 HTTP 请求时,多线程会因 OS 线程开销变大而降低效率;而 asyncio 在单线程内调度协程,内存占用低、上下文切换快。
但注意:Flask 本身是同步框架,不原生支持 async def route。强行混用会导致 RuntimeWarning: coroutine 'xxx' was never awaited 或响应丢失。
- 若坚持用 Flask,只能把异步逻辑包装成同步调用:
asyncio.run(coro())(仅限简单场景,每次调用都启停事件循环,性能差) - 真正推荐方案:换用
FastAPI或Quart(Flask 的异步兼容版本),它们原生支持async/await路由 - 如果必须用 Flask,可将耗时异步任务卸载到独立服务(如 Celery + Redis),Web 层只负责触发和轮询状态
别忽略 GIL 和 CPU 密集型任务的陷阱
Python 的 GIL 让多线程无法真正并行执行 CPU 密集型代码(如图像处理、加密计算)。此时开 10 个线程跟 1 个线程耗时几乎一样。
IO 密集型任务(HTTP、数据库查询、文件读写)不受 GIL 限制,适合用线程;CPU 密集型任务应改用 multiprocessing 或交由 C 扩展处理。
- 判断是否为 IO 密集型:看任务是否大部分时间在等系统调用返回(
select、recv、read) - 用
time.time()和time.perf_counter()对比实际耗时与 wall-clock 时间,确认瓶颈类型 - Flask 中混合使用
ThreadPoolExecutor(IO)和ProcessPoolExecutor(CPU)是可行的,但进程间通信成本高,需权衡
真正容易被忽略的是:线程池大小不是越大越好。设成 CPU 核心数的 4–5 倍对纯 IO 场景常是合理起点,但要结合目标服务的连接池上限(如 requests.adapters.HTTPAdapter(pool_connections=10))一起调优。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!











