You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Sanic服务器:await含循环函数时请求无法并发的解决咨询

解决Sanic中CPU密集型for循环导致请求无法并发的问题

我明白你的困扰——你以为给encode函数加了await就能让它异步不阻塞,但实际上你的for循环是纯CPU密集的同步代码,await根本帮不上忙,Sanic的单线程事件循环会被这个循环死死占住,直到它执行完才能处理下一个请求。

核心原因拆解

异步框架的事件循环靠的是任务主动交出控制权来实现并发,比如IO操作等待时会把控制权还给循环。但纯CPU密集的同步代码(比如你的for循环)不会有任何交出控制权的动作,事件循环只能等着它跑完,自然没法处理其他请求。

正确解决方案:把CPU任务移到执行器中

我们需要把CPU密集型任务放到线程池或进程池里执行,这样事件循环可以暂时放下这个任务,去处理其他请求,等执行池里的任务完成后再回来处理结果。

基础实现(线程池)

假设你的原代码是这样的:

from sanic import Sanic, response

app = Sanic("TestApp")

def encode():
    # 模拟CPU密集型业务操作
    result = 0
    for i in range(10**8):
        result += i
    return result

@app.route("/")
async def test(request):
    # 这里加await也没用,因为encode是同步函数
    res = await encode()
    return response.text(f"Result: {res}")

if __name__ == "__main__":
    app.run(host="0.0.0.0", port=8000)

改成异步兼容版本,用loop.run_in_executor把同步任务包装成可await的协程:

from sanic import Sanic, response
import asyncio

app = Sanic("TestApp")

def encode():
    # 保留原有的CPU密集型for循环
    result = 0
    for i in range(10**8):
        result += i
    return result

@app.route("/")
async def test(request):
    # 获取当前事件循环
    loop = asyncio.get_event_loop()
    # 把同步任务提交到默认线程池执行,返回可await的对象
    res = await loop.run_in_executor(None, encode)
    return response.text(f"Result: {res}")

if __name__ == "__main__":
    app.run(host="0.0.0.0", port=8000)

进阶优化(进程池)

如果你的CPU任务非常重,受限于Python的GIL,线程池的提升有限,可以自定义进程池避开GIL限制:

from concurrent.futures import ProcessPoolExecutor
from sanic import Sanic, response
import asyncio

app = Sanic("TestApp")
# 创建进程池,数量建议和CPU核心数一致
executor = ProcessPoolExecutor(max_workers=4)

def encode():
    result = 0
    for i in range(10**8):
        result += i
    return result

@app.route("/")
async def test(request):
    loop = asyncio.get_event_loop()
    # 提交到自定义进程池执行
    res = await loop.run_in_executor(executor, encode)
    return response.text(f"Result: {res}")

if __name__ == "__main__":
    app.run(host="0.0.0.0", port=8000)

验证效果

现在你再在两个控制台同时执行time curl http://0.0.0.0:8000/,会发现两个请求的耗时基本一致,而不是串行等待(第一个执行完第二个才开始),说明请求已经可以并发处理了。

内容的提问来源于stack exchange,提问作者Jason

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.20 07:15:32