asyncio异步IO--协程(Coroutine)与任务(Task)详解 (3)

如果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:

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), return_exceptions=True )) asyncio.run(main())

执行结果如下:

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对象的执行。
例如:

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*: task1 = asyncio.create_task(division(0, 2)) task2 = asyncio.create_task(division(1, 5)) task3 = asyncio.create_task(division(3, 6)) t = asyncio.gather( task1, task2, task3, return_exceptions=True ) task1.cancel() print(await t) asyncio.run(main())

预期执行结果如下:

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 1 res = await shield(something())

同代码:

#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())

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/zyzspx.html