python - 当 asyncio 任务在创建后被存储时,来自任务的异常被静音

标签 python python-asyncio

我在一个项目中使用 asyncio,遇到了这种奇怪的行为。

import asyncio

def schedule_something():
    global f
    tsk = asyncio.async(do_something())
    f = tsk #If this line is commented out, exceptions can be heard.

@asyncio.coroutine
def do_something():
    raise Exception()

loop = asyncio.get_event_loop()
loop.call_soon(schedule_something)
loop.run_forever()
loop.close()

出于某种原因,当您调用 asyncio.async() 时存储生成的任务会停止执行任何操作的异常。

有人可以阐明这种情况吗?我需要有一种方法来捕获当前项目中的异常。

最佳答案

这是因为只有当 Task 被销毁而没有检索到它的结果时才会引发异常。当您将 Task 分配给全局变量时,它将始终有一个事件引用,因此永远不会被销毁。 asyncio/futures.py 中有一个文档字符串对此进行了详细说明:

class _TracebackLogger:
    """Helper to log a traceback upon destruction if not cleared.

    This solves a nasty problem with Futures and Tasks that have an
    exception set: if nobody asks for the exception, the exception is
    never logged.  This violates the Zen of Python: 'Errors should
    never pass silently.  Unless explicitly silenced.'

    However, we don't want to log the exception as soon as
    set_exception() is called: if the calling code is written
    properly, it will get the exception and handle it properly.  But
    we *do* want to log it if result() or exception() was never called
    -- otherwise developers waste a lot of time wondering why their
    buggy code fails silently.

    An earlier attempt added a __del__() method to the Future class
    itself, but this backfired because the presence of __del__()
    prevents garbage collection from breaking cycles.  A way out of
    this catch-22 is to avoid having a __del__() method on the Future
    class itself, but instead to have a reference to a helper object
    with a __del__() method that logs the traceback, where we ensure
    that the helper object doesn't participate in cycles, and only the
    Future has a reference to it.

    The helper object is added when set_exception() is called.  When
    the Future is collected, and the helper is present, the helper
    object is also collected, and its __del__() method will log the
    traceback.  When the Future's result() or exception() method is
    called (and a helper object is present), it removes the the helper
    object, after calling its clear() method to prevent it from
    logging.

如果你想查看/处理异常,只需使用 add_done_callback处理任务的结果,并在遇到异常时执行任何必要的操作:

import asyncio

def handle_result(fut):
    if fut.exception():
        fut.result()  # This will raise the exception.

def schedule_something():
    global f
    tsk = asyncio.async(do_something())
    tsk.add_done_callback(handle_result)
    f = tsk

@asyncio.coroutine
def do_something():
    raise Exception()

loop = asyncio.get_event_loop()
loop.call_soon(schedule_something)
loop.run_forever()
loop.close()

关于python - 当 asyncio 任务在创建后被存储时,来自任务的异常被静音,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/27297638/

相关文章:

python - 切片列表中的所有字符串?

python - 从列表指向字典变量

python - 使用 asyncio_mongo 在 mongodb 上插入

python - 多个异步上下文管理器

python - 为什么使用 Asyncio 没有减少 Python 的整体执行时间和并发运行函数?

python - OpenMP 和 Cython 缺乏加速和错误结果

python - Python 中的模块化算法

python - 在 Python 中比较列表和省略号

python - python 中循环中的一个等待和任务

python - asyncio 中的坏锁优化