我的问题来自这篇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 序列中的其他可等待项不会被取消并将继续运行”的文档不符。能解释一下原因吗?
任务继续运行,但您的程序通过返回传递给
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())