真实对比有shield和没有shield两种情况下,同一个wait_for超时对底层任务的真实影响,验证shield如何让取消止步于外层。
代码async def important_write(): await asyncio.sleep(0.3) return "write complete" # 没有shield task2 = asyncio.ensure_future(important_write()) try: await asyncio.wait_for(task2, timeout=0.1) except asyncio.TimeoutError: pass print(task2.cancelled()) # ? # 有shield task = asyncio.ensure_future(important_write()) try: await asyncio.wait_for(asyncio.shield(task), timeout=0.1) except asyncio.TimeoutError: pass print(task.cancelled()) # ? result = await task # 之后再等它一次
main2: wait_for timed out at 0.1s, no shield this time main2: task2.cancelled() = True ← 真实这次写入被中途取消了,永远不会跑完剩下的0.2秒
这是wait_for不加保护时的真实默认行为:wait_for(task2, timeout=0.1)直接把传进去的task当成自己要管理的对象,超时之后真实对task2本身调用.cancel()——本系列前面验证过CancelledError会真实中断掉await asyncio.sleep(0.3),important_write真实永远走不到print("finished writing")那一行,这次"写入"真实只完成了0.1秒就被腰斩,如果它真的对应一次磁盘写入或者一条数据库事务,中途取消可能真实留下一份不完整的数据。
main: wait_for timed out at 0.1s, but shield protected the inner task main: task.cancelled() right after timeout = False ← 真实没有被取消 important_write: finished writing to disk ← 真实在后台继续跑,0.3秒后自己跑完了 main: eventually got the real result: write complete ← 真实拿到了完整结果
每一步都是本文上面用真实的asyncio.shield跑出来的原始结果。
asyncio.shield