Python3.6中asyncio.wait_for超时未取消任务引发RuntimeError求助
Python 3.6 asyncio.wait_for超时后任务未取消问题解答
问题原因
- Python 3.6版本的
asyncio.wait_for存在实现缺陷:当等待超时触发后,仅会向外抛出TimeoutError异常,但不会正确取消内部包裹的协程任务。代码中第一次调用的reader.read(1)协程在超时后仍然处于挂起等待数据的状态,没有被销毁。 - asyncio的
StreamReader设计上不支持并发读取操作,内部有专门的状态校验逻辑:当检测到已有一个读协程处于等待数据的状态时,新发起的读调用就会抛出你遇到的RuntimeError: read() called while another coroutine is already waiting for incoming data异常。
解决办法
- 方案1:升级Python版本到3.7及以上。Python 3.7及之后的官方版本已经修复了
asyncio.wait_for的这个缺陷,超时后会自动取消内部等待的协程,你现有的代码不需要做任何修改即可正常运行。 - 方案2:如果必须兼容Python 3.6环境,可以手动将读操作封装为Task,超时后显式取消Task并等待其取消完成,修改后的代码示例如下:
import sys import asyncio import socket async def test_single_call(): reader, writer = await asyncio.open_connection(host='127.0.0.1', port=8888) # 把读操作封装为独立Task read_task = asyncio.ensure_future(reader.read(1)) try: msg = await asyncio.wait_for(read_task, timeout=3) print("Unexcepted message received:" , msg, file=sys.stderr) assert False except asyncio.TimeoutError: # 超时后手动取消读任务 read_task.cancel() # 等待任务取消完成,处理取消异常 try: await read_task except asyncio.CancelledError: pass msg = await reader.read(1) loop = asyncio.get_event_loop() loop.run_until_complete(test_single_call()) loop.close()
内容的提问来源于stack exchange,提问作者bzhu
相关产品推荐
相关产品推荐

