Python 捕获单个任务中的异常并重新启动它们

Python 捕获单个任务中的异常并重新启动它们,python,python-3.x,python-3.6,python-asyncio,Python,Python 3.x,Python 3.6,Python Asyncio,如果我在顶级类中创建了一系列异步IO任务,所有这些任务基本上都应该永远运行,如下所示: self.event\u loop.create\u任务。。。 self.event\u loop.create\u任务。。。 self.event\u loop.create\u任务。。。 ... self.event\u loop.run\u永远 一旦退出事件循环,收集所有剩余任务, 取消它们,并终止asyncio事件循环 任务=asyncio.Task.all_任务 group=asyncio.gath

如果我在顶级类中创建了一系列异步IO任务,所有这些任务基本上都应该永远运行,如下所示:

self.event\u loop.create\u任务。。。 self.event\u loop.create\u任务。。。 self.event\u loop.create\u任务。。。 ... self.event\u loop.run\u永远 一旦退出事件循环,收集所有剩余任务, 取消它们,并终止asyncio事件循环 任务=asyncio.Task.all_任务 group=asyncio.gather*任务,返回\u异常=True 组。取消 self.event\u loop.run\u直到完成组 self.event\u loop.close 上面的代码无法处理以下情况,我发现我需要的越来越多,而且我在Google或asyncio文档中没有看到示例:

如果其中一个任务因异常而失败,则该异常不会得到处理—所有其他任务都会继续,但该任务只是在异常输出之外静默地停止

那么,我怎样才能:

设置要处理的异常,以便故障不再是无声的 最重要的是,重新启动失败的任务,有效地运行self.event\u loop.create\u task。。。再说一遍,就为了那个任务?这似乎需要在事件循环中找到接收到异常的任务,将其删除,然后添加一个新的异常-我不清楚如何做。 允许没有问题的任务不间断地继续执行。希望避免处理收到异常的任务的任何副作用。
未捕获的异常附加到任务对象,可以通过从中检索。self.event\u循环。create\u任务。。。调用返回任务对象,因此您希望收集这些对象以检查异常

如果您希望在发生异常时重新调度任务,那么您可能希望在新任务中执行此操作,因为您希望它在事件循环中运行,或者使用捕获异常的包装器协例程,然后再次重新运行给定的协程

后者可能看起来像:

import traceback

def rerun_on_exception(coro, *args, **kwargs):
    while True:
        try:
            await coro(*args, **kwargs)
        except asyncio.CancelledError:
            # don't interfere with cancellations
            raise
        except Exception:
            print("Caught exception")
            traceback.print_exc()
然后,在将协同程序安排为任务时,使用上述协同程序包装它们:

self.event_loop.create_task(rerun_on_exception(coroutine_uncalled, arg1value, ... kwarg1=value, ...)
e、 g.每次出现异常时,传入参数以创建协同路由

另一个选项是在单独的任务中使用,以便在循环运行时监视异常,并决定如何处理异常,然后:

def exception_aware_scheduler(*task_definitions, loop=None):
    if loop is None:
        loop = asyncio.get_event_loop()

    task_arguments = {
        loop.create_task(coro(*args, **kwargs)): (coro, args, kwargs)
        for coro, args, kwargs in task_definitions
    }
    while tasks:
        done, pending = await asyncio.wait(
            tasks.keys(), loop=loop, return_when=asyncio.FIRST_EXCEPTION
        )
        for task in done:
            if task.exception() is not None:
                print('Task exited with exception:')
                task.print_stack()
                print('Rescheduling the task\n')
                coro, args, kwargs = tasks.pop(task)
                tasks[loop.create_task(coro(*args, **kwargs))] = coro, args, kwargs
当任何一个计划的任务由于异常而退出时,事件循环会再次给予asyncio.wait调用控制权,但在此之前,任务可能已被取消或仅完成其工作。当某个任务确实因为异常而退出时,您需要一种方法来使用相同的参数再次创建相同的协同路由,因此上面的*args、**kwargs设置

您只需要安排异常\u感知\u调度程序,传入您想要传入的任务:

tasks = (
    (coro1, (), {}),  # no arguments
    (coro2, ('arg1', 'arg2'), {}),
    # ...
)
loop.create_task(exception_aware_scheduler(*task_definitions, loop=loop))

未捕获的异常附加到任务对象,可以通过从中检索。self.event\u循环。create\u任务。。。调用返回任务对象,因此您希望收集这些对象以检查异常

如果您希望在发生异常时重新调度任务,那么您可能希望在新任务中执行此操作,因为您希望它在事件循环中运行,或者使用捕获异常的包装器协例程,然后再次重新运行给定的协程

后者可能看起来像:

import traceback

def rerun_on_exception(coro, *args, **kwargs):
    while True:
        try:
            await coro(*args, **kwargs)
        except asyncio.CancelledError:
            # don't interfere with cancellations
            raise
        except Exception:
            print("Caught exception")
            traceback.print_exc()
然后,在将协同程序安排为任务时,使用上述协同程序包装它们:

self.event_loop.create_task(rerun_on_exception(coroutine_uncalled, arg1value, ... kwarg1=value, ...)
e、 g.每次出现异常时,传入参数以创建协同路由

另一个选项是在单独的任务中使用,以便在循环运行时监视异常,并决定如何处理异常,然后:

def exception_aware_scheduler(*task_definitions, loop=None):
    if loop is None:
        loop = asyncio.get_event_loop()

    task_arguments = {
        loop.create_task(coro(*args, **kwargs)): (coro, args, kwargs)
        for coro, args, kwargs in task_definitions
    }
    while tasks:
        done, pending = await asyncio.wait(
            tasks.keys(), loop=loop, return_when=asyncio.FIRST_EXCEPTION
        )
        for task in done:
            if task.exception() is not None:
                print('Task exited with exception:')
                task.print_stack()
                print('Rescheduling the task\n')
                coro, args, kwargs = tasks.pop(task)
                tasks[loop.create_task(coro(*args, **kwargs))] = coro, args, kwargs
当任何一个计划的任务由于异常而退出时,事件循环会再次给予asyncio.wait调用控制权,但在此之前,任务可能已被取消或仅完成其工作。当某个任务确实因为异常而退出时,您需要一种方法来使用相同的参数再次创建相同的协同路由,因此上面的*args、**kwargs设置

您只需要安排异常\u感知\u调度程序,传入您想要传入的任务:

tasks = (
    (coro1, (), {}),  # no arguments
    (coro2, ('arg1', 'arg2'), {}),
    # ...
)
loop.create_task(exception_aware_scheduler(*task_definitions, loop=loop))

两个选择-太棒了!我会研究这个,然后再告诉你哪一个合适,或者如果我有进一步的建议,但我认为这看起来正是我想要的。敬请期待。两个选择-太棒了!我会研究这个,然后再告诉你哪一个合适,或者如果我有进一步的建议,但我认为这看起来正是我想要的。敬请期待。