【问题标题】:trio nursery that doesn't cancel all tasks if one fails如果一个失败不会取消所有任务的三重奏托儿所
【发布时间】:2018-01-20 10:18:46
【问题描述】:

我想用 trio 实现一个服务器。单个客户端连接由托儿所产生的任务处理。然而,三位文档说“如果托儿所内的任何任务以未处理的异常结束,那么托儿所会立即取消托儿所内的所有任务。”。这对我的用例来说是非常不幸的。我宁愿在记录错误时继续为其他连接提供服务。有没有办法做到这一点?

【问题讨论】:

    标签: python python-trio


    【解决方案1】:

    您可以自己实现 Nursery 接口:

    class ExceptionLoggingNursery:
        def __init__(self, nursery):
            self.nursery = nursery
    
        @property
        def cancel_scope(self):
            return self.nursery.cancel_scope
    
        async def _run_and_log_errors(self, async_fn, *args):
            # This is more cumbersome than it should be
            # See https://github.com/python-trio/trio/issues/408
            def handler(exc):
                if not isinstance(exc, Exception):
                    return exc
                logger.error("Unhandled exception!", exc_info=exc)
            with trio.MultiError.catch(handler):
                return await async_fn(*args)
    
        def start_soon(self, async_fn, *args, **kwargs):
            self.nursery.start_soon(self._run_and_log_errors, async_fn, *args, **kwargs)
    
        async def start(self, async_fn, *args, **kwargs):
            return await self.nursery.start(self._run_and_log_errors, async_fn, *args, **kwargs)
    
    @asynccontextmanager
    async def open_exception_logging_nursery():
        async with trio.open_nursery() as nursery:
            yield ExceptionLoggingNursery(nursery)
    

    请注意,我们只捕获Exception 子类,并允许其他异常继续传播。这意味着,如果您的一个子任务引发KeyboardInterrupt(因为您按下了 control-C)或trio.Cancelled(因为您,嗯,取消了它......也许是因为您按下了 control-C 和托儿所parent 居住的对象被取消了),然后这些异常被允许传播出去,仍然导致所有其他任务被取消,这几乎肯定是你想要的。

    这是一些代码,但它可以很容易地放入可重用的库中。 (如果我真的这样做,我可能会将异常处理代码作为传递给open_exception_logging_nursery 的参数,而不是硬编码对logger.error 的调用。)而且我很想看到一个有这种类型的库其中的“智能主管”——基本的三人组托儿所总是旨在作为此类事情的基石。您也可以想象其他更有趣的策略,例如“如果任务因未处理的异常而退出,则记录一些内容,然后以指数退避方式重新启动它”。 (Erlang supervisors 是一个很好的灵感来源,可以窃取灵感。)

    【讨论】:

    • 此代码有问题:current_task().parent_nursery 不返回此自定义托儿所。也许我不应该使用current_task().parent_nursery...
    • 确实,parent_nursery 确实是为调试器准备的,所以它显示了底层的现实,打破了这种抽象。对于常规的非调试器代码,我建议绕过托儿所而不是使用parent_nursery
    猜你喜欢
    • 1970-01-01
    • 2020-03-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-03-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多