当异常发生时,协程会在 asyncio.gather(*aws, return_exceptions=False) 中停止

问题描述 投票:0回答:1

我的问题来自这篇post。我会在这里描述它。

我有以下Python代码:

import asyncio, time

async def fn1(x):
    await asyncio.sleep(3) 
    print(f"fn1 is called with x={x}")
    return "fn1 SUCCESS"

async def fn2(y):
    await asyncio.sleep(2) 
    print(f"fn2 is called with y={y}")
    raise asyncio.TimeoutError() 
    print(y, '*'*10)
    return "fn2 SUCCESS"

async def main():
    print("start:",time.ctime())
    result = ['No results']
    try:
        result = await asyncio.gather(
            fn1("fn1"),
            fn2("fn2"),
            return_exceptions=False, 
            # return_exceptions=True, 
            )
    except Exception as e:
        print('e:', type(e), str(e))
    print("end:",time.ctime())
    print(result)

asyncio.run(main())

这是我得到的结果:

start: Mon Sep 30 17:25:28 2024
fn2 is called with y=fn2
e: <class 'TimeoutError'> 
end: Mon Sep 30 17:25:30 2024
['No results']

根据

awaitable asyncio.gather(*aws, return_exceptions=False)¶

如果 return_exceptions 为 False(默认),则第一个引发的异常是 立即传播到等待 Gather() 的任务。 aws 序列中的其他可等待任务不会被取消,并将继续运行。

但这与我得到的结果相反。 为什么

fn1("fn1")
协程无法完成运行?如果完成运行,
print(f"fn1 is called with x={x}")
行应该将其打印出来。

然后,我对

fn1
做了一个简单的更改,其中“睡眠”时间更短了:

async def fn1(x):
    await asyncio.sleep(3-1) 
    print(f"fn1 is called with x={x}")
    return "fn1 SUCCESS"

此时协程

fn1
即将完成:

start: Mon Sep 30 17:30:29 2024
fn1 is called with x=fn1
fn2 is called with y=fn2
e: <class 'TimeoutError'> 
end: Mon Sep 30 17:30:31 2024
['No results']

这对我来说是出乎意料的!这里的行为似乎与“aws 序列中的其他可等待项不会被取消并将继续运行”的文档不符。能解释一下原因吗?

python asynchronous async-await python-asyncio
1个回答
0
投票

任务继续运行,但您的程序通过返回传递给

asyncio.run
的协同例程立即完成。 只有当控件返回到事件循环时它才能真正执行。

只需在

await asyncio.sleep(2)
中、
main
之前添加
print("end")
,您就会看到来自
fn1
的打印。当然它的返回值不会包含在
results
中,因为
.gather
已经返回了。

如果你想获取它的返回值,你必须手动将对

fn1
的调用包装到任务中,而不是将协同例程直接传递给
.gather
(在这种情况下,它会创建包装任务,但有没有直接的方法来获取它的引用):

...

async def main():
    print("start:",time.ctime())
    result = ['No results']
    try:
        result = await asyncio.gather(
            fn1_task:=asyncio.create_task(fn1("fn1")),
            fn2("fn2"),
            return_exceptions=False, 
            # return_exceptions=True, 
            )
    except Exception as e:
        print('e:', type(e), str(e))
    print(await fn1_task)
    print("end:",time.ctime())
    print(result)

asyncio.run(main())
© www.soinside.com 2019 - 2024. All rights reserved.