这是代码,我认为程序会立即崩溃,因为这是不寻常的例外。然而,当主要任务coro2
完成时,它等待了10秒。
import asyncio
@asyncio.coroutine
def coro1():
print("coro1 primed")
yield
raise Exception("abc")
@asyncio.coroutine
def coro2(loop):
try:
print("coro2 primed")
ts = [asyncio.Task(coro1(),loop=loop) for _ in range(2)]
res = yield from asyncio.sleep(10)
print(res)
except Exception as e:
print(e)
raise
loop= asyncio.get_event_loop()
loop.run_until_complete(coro2(loop))
我认为这是一个严重的问题,因为在更复杂的程序中,这会使进程永远停滞不前,而不是因为异常信息而崩溃。
此外,我在except
块中在run_until_complete
源代码中设置了一个断点,但没有触发。我感兴趣的是哪个代码在python异步中处理了该异常。
发布于 2020-12-19 13:07:08
首先,没有理由在Python中使用具有多年来可用的异步/等待语法的基于生成器的协同器,而coroutine
装饰器现在已不再推荐并计划删除。另外,您不需要将事件循环向下传递到每个协同线,您可以在需要时使用asyncio.get_event_loop()
来获得它。但这些和你的问题无关。
except
块在coro2
中没有触发,因为在coro1
中引发的异常没有传播到coro2
。这是因为您显式地将coro1
作为一个任务运行,它在后台执行它,而不是等待它。您应该始终确保您的任务被等待,然后异常不会被忽略;系统地这样做有时被称为结构化并发。
编写以上内容的正确方法如下:
async def coro1():
print("coro1 primed")
await asyncio.sleep(0) # yield to the event loop
raise Exception("abc")
async def coro2():
try:
print("coro2 primed")
ts = [asyncio.create_task(coro1()) for _ in range(2)]
await asyncio.sleep(10)
# ensure we pick up results of the tasks that we've started
for t in ts:
await t
print(res)
except Exception as e:
print(e)
raise
asyncio.run(coro2())
请注意,这将运行sleep()
直到完成,然后才会传播由后台任务引发的异常。如果您想立即进行传播,可以使用asyncio.gather()
,在这种情况下,您首先不必费心显式地创建任务:
async def coro2():
try:
print("coro2 primed")
res, *ignored = await asyncio.gather(
asyncio.sleep(10),
*[(coro1()) for _ in range(2)]
)
print(res)
except Exception as e:
print(e)
raise
我感兴趣的是哪个代码在python异步中处理了该异常。
由coroutine引发的未被处理的异常被异步捕获并存储在任务对象中。这允许您等待任务,或者(如果您知道任务已经完成)使用result()
方法获得其结果,其中任一方法将传播(重新引发)异常。由于您的代码从未访问任务的结果,因此异常实例在任务对象中仍然被遗忘。Python注意到了这一点,并在任务对象被销毁时打印了一个“任务异常从未检索过”警告,但是这个警告是在尽力而为的基础上提供的,通常来得太晚,不应该依赖。
https://stackoverflow.com/questions/65369022
复制相似问题