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

uWSGI线程留存事件循环导致Django SynchronousOnlyOperation异常如何解决

根本原因
  • asyncio的事件循环默认和线程绑定,存储在线程的本地上下文里,只要线程不销毁,绑定的事件循环对象就会一直留存。你使用的uWSGI采用常驻工作线程模式,请求处理完成后线程不会退出,会继续接收后续请求。
  • 你的AsyncProvider初始化逻辑会优先获取当前线程已绑定的事件循环,不存在才新建,且全程没有主动解绑线程上下文事件循环的操作。线程1第一次处理/createloop请求时就被绑定了事件循环,后续该线程处理任何请求时,上下文都会携带这个事件循环。
  • Django 3.x+的同步ORM会主动检测当前线程是否存在存活状态的事件循环,只要检测到就会判定当前处于异步上下文,禁止直接调用同步ORM接口,抛出SynchronousOnlyOperation异常。
修复方案

方案1:用完事件循环后主动清理(最适配现有代码)

修改AsyncProvider逻辑,每次使用独立的事件循环,用完后主动关闭并解绑线程上下文,避免循环残留:

import asyncio
import aiohttp

class AsyncProvider:
  def __init__(self):
    # 直接新建独立循环,不获取线程已绑定的循环
    self.__loop = asyncio.new_event_loop()
    self.__session = aiohttp.ClientSession(loop=self.__loop)

  # 你原有的异步任务执行逻辑
  def run_async_task(self, coro):
    try:
      return self.__loop.run_until_complete(coro)
    finally:
      # 关闭会话和循环
      self.__loop.run_until_complete(self.__session.close())
      self.__loop.close()
      # 清除线程绑定的循环,彻底消除残留
      asyncio.set_event_loop(None)

方案2:独立线程托管事件循环(高并发场景推荐)

单独开一个常驻线程专门跑事件循环,所有异步操作都提交到该线程执行,完全不污染处理Django请求的工作线程:

import asyncio
import threading
import aiohttp
import time

# 全局托管的事件循环和会话
_loop = None
_session = None

def _run_event_loop():
    global _loop
    _loop = asyncio.new_event_loop()
    asyncio.set_event_loop(_loop)
    _loop.run_forever()

# 应用启动时启动独立的循环线程
threading.Thread(target=_run_event_loop, daemon=True).start()
# 等待循环初始化完成
while _loop is None:
    time.sleep(0.01)
_session = aiohttp.ClientSession(loop=_loop)

class AsyncProvider:
    def run_async_task(self, coro):
        future = asyncio.run_coroutine_threadsafe(coro, _loop)
        return future.result()

方案3:临时关闭Django检测(应急用,不推荐长期使用)

如果是老项目临时救急,可以在settings.py中添加配置关闭异步上下文检测,该方案有线程安全风险,仅适合临时过渡:

# settings.py
DJANGO_ALLOW_ASYNC_UNSAFE = True

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.28 22:36:07