问题
我有几个连续运行的任务,但其中一个偶尔需要重新启动,所以这个任务在后台运行。如何立即引发来自这个后台任务的异常?在下面的示例中,直到下一次尝试重新启动时才会引发异常,这在真实的应用程序中可能非常罕见,因此可能长时间不被注意。
示例
https://replit.com/@PatrickPei/how-to-raise-exceptions-in-python-asyncio-background-task
此示例运行3个任务:
foo
,不引发异常bar
,在6次迭代后引发异常on_interval
,每5秒重新启动一次bar
import asyncio
task = None
i = 0
async def foo():
while True:
print("foo")
await asyncio.sleep(1)
async def bar():
while True:
global i
i += 1
if i > 4:
raise ValueError()
print("bar", i)
await asyncio.sleep(1)
async def on_interval(n):
while True:
await asyncio.sleep(n)
# Cancel task.
global task
print("Canceling bar")
task.cancel()
try:
await task
except asyncio.CancelledError:
pass
# Restart task.
print("Restarting bar")
task = asyncio.create_task(bar())
async def main():
# Start background task.
print("Starting bar")
global task
task = asyncio.create_task(bar())
# Start other tasks.
await asyncio.gather(
foo(),
on_interval(3),
)
if __name__ == "__main__":
asyncio.run(main())
输出
bar
迭代4次并引发一个异常,直到下一次重新启动才捕获该异常,如bar 4
之后的3次foo
迭代所示。当两次重新启动之间有大量时间时,这是一个问题,因为异常长时间未被注意到。
Starting bar
bar 1
foo
bar 2
foo
bar 3
foo
Canceling bar
Restarting bar
bar 4
foo
foo
foo
Canceling bar
Traceback (most recent call last):
File "~/example.py", line 60, in <module>
asyncio.run(main())
File "~/.pyenv/versions/3.10.5/lib/python3.10/asyncio/runners.py", line 44, in run
return loop.run_until_complete(main)
File "~/.pyenv/versions/3.10.5/lib/python3.10/asyncio/base_events.py", line 646, in run_until_complete
return future.result()
File "~/example.py", line 53, in main
await asyncio.gather(
File "~/example.py", line 37, in on_interval
await task
File "~/example.py", line 22, in bar
raise ValueError()
ValueError
次尝试
- 启动了另一个任务来检查
asyncio.Task.exception
,但这很麻烦,因为每个后台任务都需要另一个忙碌循环来帮助引发其异常。 - 已尝试
asyncio.Task.add_done_callback
,但由于后台任务仍等待下次重新启动,因此它仅记录错误,而不会停止其它任务foo
。
2条答案
按热度按时间carvr3hs1#
在python3.11中,使用异步上下文管理器
async with
和asyncio.TaskGroup()
可以简单地解决这个问题。slhcrj9b2#
将可能生成异常代码 Package 在try catch块中。