2014-12-04 48 views
4

我对项目使用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()停止做任何异常。

难道有人会对这种情况有所了解吗?我需要一种方法来捕获当前项目中的异常。

回答

3

这是因为只有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() 
+0

谢谢,这实际上使我的项目更容易,指定一个特定任务的异常处理程序,而不是事件循环中的任何异常。 – 2014-12-04 17:24:35