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

Python中如何复用asyncio事件循环?解决aiohttp会话已关闭报错

错误原因
  • 每次调用asyncio.run()都会启动一个全新的事件循环,执行完传入的协程后会立即关闭当前循环
  • pymyq登录后生成的aiohttp会话、设备实例都会和第一次运行的事件循环绑定,第二次调用asyncio.run()启动的新循环无法访问旧循环中的会话资源,因此抛出RuntimeError: Session is closed
  • 不需要创建多个事件循环,全程复用同一个事件循环即可解决问题
适配Flask运行环境的解决方案

Flask属于持续运行的服务,不能每次调用异步方法都重启事件循环,根据你使用的Flask版本可以选择以下两种方案:

方案1:使用异步Flask(2.0+版本,推荐)

Flask 2.0及以上版本原生支持异步路由,可以直接复用Flask启动的全局事件循环,无需手动管理循环生命周期:

from flask import Flask
import pymyq

app = Flask(__name__)
# 全局保存登录后的设备实例,避免重复登录
myq_device = None

class MyObject:
    async def login(self):
        global myq_device
        api = await pymyq.login("你的账号", "你的密码")
        devices = await api.get_devices()
        myq_device = next(iter(devices.values()))
    
    async def set_state(self, open_state: bool):
        if not myq_device:
            await self.login()
        if open_state:
            await myq_device.open()
        else:
            await myq_device.close()

my_obj = MyObject()

# 异步路由直接调用异步方法
@app.route("/login")
async def login_api():
    await my_obj.login()
    return "登录成功"

@app.route("/open")
async def open_api():
    await my_obj.set_state(True)
    return "设备已开启"

@app.route("/close")
async def close_api():
    await my_obj.set_state(False)
    return "设备已关闭"

方案2:兼容同步Flask版本

如果使用的是不支持异步的旧版Flask,可以后台启动一个全局复用的事件循环,所有异步协程都提交到该循环执行:

import asyncio
import threading
from flask import Flask
import pymyq

app = Flask(__name__)
myq_device = None

# 后台启动全局事件循环,全程复用
loop = asyncio.new_event_loop()
def run_loop():
    asyncio.set_event_loop(loop)
    loop.run_forever()
threading.Thread(target=run_loop, daemon=True).start()

class MyObject:
    def login(self):
        global myq_device
        async def _login_coro():
            api = await pymyq.login("你的账号", "你的密码")
            devices = await api.get_devices()
            nonlocal myq_device
            myq_device = next(iter(devices.values()))
        # 提交协程到全局循环执行,阻塞等待结果返回
        asyncio.run_coroutine_threadsafe(_login_coro(), loop).result()
    
    def set_state(self, open_state: bool):
        if not myq_device:
            self.login()
        async def _set_coro():
            if open_state:
                await myq_device.open()
            else:
                await myq_device.close()
        asyncio.run_coroutine_threadsafe(_set_coro(), loop).result()

my_obj = MyObject()

# 同步路由正常调用方法
@app.route("/login")
def login_api():
    my_obj.login()
    return "登录成功"

@app.route("/open")
def open_api():
    my_obj.set_state(True)
    return "设备已开启"

@app.route("/close")
def close_api():
    my_obj.set_state(False)
    return "设备已关闭"
注意事项
  • 不要在服务类方法中频繁调用asyncio.run(),该方法设计定位是程序单次入口调用,不适合作为多次异步执行的工具方法
  • 全局复用的事件循环会保留pymyq生成的会话资源,只要不手动关闭循环,会话会一直有效

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.04 21:15:01