问题
我有几个持续运行的任务,但其中一个偶尔需要重新启动,因此这个任务在后台运行。如何立即引发来自此后台任务的异常?在以下示例中,异常直到下一次尝试重新启动时才被引发,这在实际应用程序中可能非常不频繁,因此可能会长时间不被注意。
示例
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
。