服务器关闭连接时,sock_recv
返回一个空的字节数组(b''
),表示文件结束。由于您不处理该条件,因此您的代码最终陷入了处理相同缓冲区的无限循环中。
if data == b'':
break
…data = await loop.sock_recv(...)
行后
但是上面仍然没有解释为什么wait_for
不能取消无赖协程。问题是await
,正如有时可以理解的那样,这并不意味着“将控制传递给事件循环”。它的意思是“从提供的等待对象中请求值, (并且只要)对象指示它没有准备好值,就将控制权交给事件循环。” 该 如果 是至关重要的:如果对象 确实 已经准备好时,首先询问,该值将被立即使用而不用推迟事件循环的值。换句话说,await
例如,下面的协程完全阻塞事件循环和防止以往任何时候都在运行,尽管它的内环由没有任何其他的协程 ,但 等待:
async def busy_loop():
while True:
await noop()
async def noop():
pass
在您的示例中,由于套接字在文件结束时根本不会阻塞,因此协程永远不会挂起,并且(与上述错误共同作用)协程永远不会退出。
为了确保其他任务有运行的机会,您可以添加await asyncio.sleep(0)
一个循环。对于大多数代码而言,这不是必需的,因为在这些情况下,请求IO数据将很快导致等待,这时将触发事件循环。(实际上,经常需要这样做表明存在设计缺陷。)仅与EOF处理错误结合在一起,使代码卡住。