如果return_execptions参数为False(默认值即为False),引发的第一个异常会立即传播给等待gather()的任务,即调用await asyncio.gather()对象。序列中其他awaitable对象的执行不会受影响。例如:
import asyncio async def division(divisor, dividend): if divisor == 0: raise ZeroDivisionError else: print(f"{dividend}/{divisor}={dividend/divisor}") return dividend/divisor async def main(): # Schedule three calls *concurrently*: print(await asyncio.gather( division(0, 2), division(1, 2), division(2, 2), )) asyncio.run(main())执行结果:
2/1=2.0 2/2=1.0 Traceback (most recent call last): File "test.py", line 19, in <module> asyncio.run(main()) File "c:\Program Files\Python37\lib\asyncio\runners.py", line 43, in run return loop.run_until_complete(main) File "c:\Program Files\Python37\lib\asyncio\base_events.py", line 573, in run_until_complete return future.result() File "test.py", line 16, in main division(2, 2), File "test.py", line 6, in division raise ZeroDivisionError ZeroDivisionError如果return_exceptions参数为True,异常会和正常结果一样,被聚合到结果列表中返回。
对以上代码稍作修改,将return_exceptions设为True:
执行结果如下:
2/1=2.0 2/2=1.0 [ZeroDivisionError(), 2.0, 1.0]#错误不会向上传播,而是作为结果返回如果gather()被取消,则提交的所有awaitable对象(尚未执行完成的)都会被取消。例如:
import asyncio async def division(divisor, dividend): if divisor == 0: raise ZeroDivisionError else: await asyncio.sleep(divisor) print(f"{dividend}/{divisor}={dividend/divisor}") return dividend/divisor async def main(): # Schedule three calls *concurrently*: t = asyncio.gather( division(0, 2), division(1, 5), division(3, 6), return_exceptions=True ) await asyncio.sleep(2) t.cancel() await t asyncio.run(main())执行结果:
5/1=5.0 #除已执行的之外,其他的任务全部被取消 Traceback (most recent call last): File "test.py", line 23, in <module> asyncio.run(main()) File "c:\Program Files\Python37\lib\asyncio\runners.py", line 43, in run return loop.run_until_complete(main) File "c:\Program Files\Python37\lib\asyncio\base_events.py", line 573, in run_until_complete return future.result() concurrent.futures._base.CancelledError #在return_exceptions=True的情况下,异常依然向上传播。如果aws中某些Task或Future被取消,gather()调用不会被取消,被取消的Task或Future会以引发CancelledError的方式被处理。这样可以避免个别awaitable对象的取消操作影响其他awaitable对象的执行。
例如:
预期执行结果如下:
5/1=5.0 6/3=2.0 [CancelledError(), 5.0, 2.0] # 仅task1被取消,其他任务不受影响。 避免取消 awaitable asyncio.shield(aw, * , loop=None)防止awaitable对象被执行。
如果aw参数是一个协程(coroutines),该对象会被自动封装为Task对象进行处理。
通常,代码:
同代码:
#code 2 res = await something()是等价的。
特殊情况是,如果包含以上代码的协程被 取消,code 1与code 2的执行效果就完全不同了:
code 1中,运行于something()中的任务 不会被取消。
code 2中,运行于something()中的任务 会被取消。
在code 1中,从something()的视角看,取消操作并没有发生。然而,事实上它的调用者确实被取消了,所以await shield(something())仍然会引发一个CancelledError异常。
import asyncio import time async def division(divisor, dividend): if divisor == 0: raise ZeroDivisionError else: await asyncio.sleep(divisor) print(f"{time.strftime('%X')}:{dividend}/{divisor}={dividend/divisor}") return dividend/divisor async def main(): # Schedule three calls *concurrently*: print(f"Start time:{time.strftime('%X')}") task1 = asyncio.shield(division(1, 2)) task2 = asyncio.create_task(division(1, 5)) task3 = asyncio.create_task(division(3, 6)) res = asyncio.gather(task1, task2, task3, return_exceptions=True) task1.cancel() task2.cancel() print(await res) asyncio.run(main())