discord.py非异步函数调用发消息异步函数触发RuntimeError问题咨询
错误原因
你触发报错的核心原因是:asyncio.run() 会创建全新的独立事件循环,但discord.py客户端启动后已经在运行专属的事件循环,所有discord相关的异步操作(比如发消息)必须在这个内置循环中执行,跨循环调用aiohttp请求就会触发"Timeout context manager should be used inside a task"报错。同时firestore的快照回调是在独立子线程中触发的,也不能直接操作主线程的事件循环。
解决方法
直接将你的异步发送任务提交到discord.py内置的事件循环中执行即可,不需要额外创建新循环:
- 优先避免异步函数名和参数名重名,防止逻辑冲突
- 使用
asyncio.run_coroutine_threadsafe跨线程提交协程任务到discord的事件循环,这个方法是线程安全的,适配firestore回调的子线程运行环境
修改后的代码示例
首先修改发送消息的异步函数,避免重名:
async def send_msg(content, channel): await channel.send(content)
然后修改checkStart函数中的调用逻辑:
def checkStart(doc_snapshot,changes,read_time): for change in changes: if change.type.name == 'ADDED' or change.type.name == 'MODIFIED': anaDoc = change.document.to_dict() if anaDoc['start'] == True: # 提交协程到discord内置事件循环执行 asyncio.run_coroutine_threadsafe( send_msg('Game started!', main_channel[change.document.reference.parent.parent.id]), client.loop ) print(change.document.reference.parent.parent.id)
补充说明
如果你不需要等待消息发送的返回结果,也可以用性能更高的调用方式:
client.loop.call_soon_threadsafe( client.create_task, send_msg('Game started!', main_channel[change.document.reference.parent.parent.id]) )
不要在discord.py运行过程中使用asyncio.run()创建新循环,否则会持续出现资源冲突类报错。
内容的提问来源于stack exchange,提问作者Francisco Meirelles
相关产品推荐
相关产品推荐

