2014-12-04 32 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