使用Python Asyncio实现WebSocket后台任务遇协程未等待警告求助
问题分析与解决方案
你遇到的RuntimeWarning: coroutine 'background_task' was never awaited错误,核心原因是直接用threading.Thread调用异步协程函数——普通线程只会创建协程对象,不会触发它的执行,异步协程必须在事件循环中通过await或调度接口启动。另外你的queue.get_next_message_from_sqs()如果是同步阻塞方法,还会卡住整个异步事件循环,导致WebSocket的其他连接、消息事件无法处理。
正确实现方式
1. 用SocketIO自带的异步任务启动协程
SocketIO的AsyncServer提供了start_background_task方法,可直接在异步上下文里调度后台协程,无需手动创建线程,自动适配ASGI的事件循环。
2. 隔离同步SQS调用(避免阻塞事件循环)
如果get_next_message_from_sqs()是同步阻塞方法,必须把它放到线程池执行,让事件循环在等待SQS响应时,能继续处理其他WebSocket事件。
修正后的完整代码
import socketio import json import asyncio from sqs_handler import SQSQueue sio = socketio.AsyncServer(async_mode='asgi') app = socketio.ASGIApp(sio, static_files={"/": "./"}) @sio.event async def connect(sid, environ): print(sid, "connected") # 确保后台任务只启动一次,避免多个客户端连接重复创建任务 if not hasattr(sio, 'bg_task_started'): sio.bg_task_started = True sio.start_background_task(background_task) @sio.event async def disconnect(sid): print(sid, "disconnected") @sio.event async def item_removed(sid, data): await sio.emit("item_removed", data) async def background_task(): queue = SQSQueue() while True: # 把同步SQS调用放到线程池,不阻塞事件循环 message = await asyncio.to_thread(queue.get_next_message_from_sqs) data = json.loads(message.body) await sio.emit('item_added', data)
关键说明
- 协程调度:
sio.start_background_task会将协程提交到当前ASGI事件循环执行,彻底解决“协程未被await”的警告。 - 非阻塞SQS调用:
asyncio.to_thread(Python 3.9+)将同步函数放到线程池运行,返回可await对象,事件循环在等待SQS响应时,能处理其他WebSocket事件。 - 单例任务:通过给
sio对象添加标记,确保后台任务仅启动一次,避免多客户端连接导致的重复任务问题。
如果你的SQS handler提供异步版本(比如boto3的异步客户端),可以直接用异步调用替代线程池,代码会更简洁:
async def background_task(): queue = SQSQueue() while True: # 直接调用异步SQS方法 message = await queue.get_next_message_from_sqs_async() data = json.loads(message.body) await sio.emit('item_added', data)
内容的提问来源于stack exchange,提问作者nycelia
相关产品推荐
相关产品推荐

