使用await asyncio.to_thread为何会阻塞事件循环,无法响应WebSocket停止信号?
为何使用await asyncio.to_thread会阻塞事件循环,无法响应WebSocket停止信号?
嗨,我来给你把这个问题讲明白~核心差异其实出在**await会暂停当前协程的执行**,而asyncio.create_task会把任务丢到后台异步运行,不阻塞当前协程的处理流程。
咱们结合你的代码一步步拆解:
1. 用await blocking时发生了什么?
当你在handle_websocket里执行这段代码时:
blocking = asyncio.to_thread(self.do_blocking_work) result = await blocking
asyncio.to_thread确实把阻塞的do_blocking_work放到了单独的线程里运行,事件循环本身并没有被卡死,但当前的handle_websocket协程会被强制暂停——它会一直等待线程里的do_blocking_work完全执行完毕,才会继续往下走。- 而你的WebSocket消息处理逻辑(
async for message in websocket)是绑定在handle_websocket这个协程里的!当协程卡在await blocking这里时,它根本没法回到async for循环去接收后续的"stop_blocking_work"消息。 - 这就形成了死局:
do_blocking_work的循环要靠self.continue_blocking_work变为False才能退出,但修改这个变量的stop消息因为handle_websocket被暂停,根本没人处理。最终线程一直循环,await永远等不到结果,自然没法响应停止信号。
2. 用asyncio.create_task(blocking)时为什么能工作?
当你换成这段代码后:
task = asyncio.create_task(blocking)
create_task会把blocking这个协程包装成一个后台任务,丢给事件循环去调度执行,而当前的handle_websocket协程不会被暂停,会立刻继续执行后面的print("after blocking"),然后回到async for message in websocket的循环,继续监听新的WebSocket消息。- 这时候当你发送"stop_blocking_work"消息,
handle_websocket能立刻接收到,把self.continue_blocking_work设为False,线程里的do_blocking_work循环就会退出,线程执行完毕后后台任务也随之完成。
关键区别总结
await 协程:当前协程会挂起等待,必须等目标任务完成才能继续,会直接阻塞当前流程的后续逻辑(比如你的WebSocket消息监听)。asyncio.create_task(协程):把任务后台异步化,当前协程不受影响可以继续处理新消息,这样才有机会触发停止信号,让线程里的工作正常结束。
备注:内容来源于stack exchange,提问作者me.at.coding
相关产品推荐
相关产品推荐

