tornado.concurrent-Future

Future的主要用途就是,存储结果。当结果返回时,就会对调注册的函数。用于支持异步,是很是方便的。
python

def async_task():
    future = Future()
    ioloop.IOLoop.current().call_later(lambda f: f.set_result('hello'))
    return future

定义对调函数,回调函数必须接受future参数。app

def callback(future):
    try:
        result = future.result()
    except:
        print "exception occured"
    print "future return result: %s" % result


注册回调函数:异步

future = async_task()
future.set_done_callback(callback)



首先来看注册回调函数的源码:async

def add_done_callback(self, fn):
        """Attaches the given callback to the `Future`.

        It will be invoked with the `Future` as its argument when the Future
        has finished running and its result is available.  In Tornado
        consider using `.IOLoop.add_future` instead of calling
        `add_done_callback` directly.
        """
        if self._done:
            fn(self)
        else:
            self._callbacks.append(fn)


self就是指向Future自身,由fn(self),能够看出回调函数的格式。ide

若是已经返回结果或者异常,就直接调用这个回调函数fn。函数

若是没有,就把fn添加到self._callbacks属性里。等到结果或者异常返回时,再调用。tornado


而后接着看,怎么返回结果:
oop

   def set_result(self, result):
        """Sets the result of a ``Future``.

        It is undefined to call any of the ``set`` methods more than once
        on the same object.
        """
        self._result = result
        self._set_done()

这里将结果保存在self._result属性里,接着调用_set_done方法。spa

def _set_done(self):
        self._done = True
        for cb in self._callbacks:
            try:
                cb(self)
            except Exception:
                app_log.exception('exception calling callback %r for %r',
                                  cb, self)
        self._callbacks = None

更新_done属性,而后依次调用self._callbacks的回调函数。code

最后跟新self._callbacks为None。


若是是返回异常:

def set_exception(self, exception):
        """Sets the exception of a ``Future.``"""
        self.set_exc_info(
            (exception.__class__,
             exception,
             getattr(exception, '__traceback__', None)))

def set_exc_info(self, exc_info):
        """Sets the exception information of a ``Future.``

        Preserves tracebacks on Python 2.

        .. versionadded:: 4.0
        """
        self._exc_info = exc_info
        self._log_traceback = True
        if not _GC_CYCLE_FINALIZERS:
            self._tb_logger = _TracebackLogger(exc_info)

        try:
            self._set_done()
        finally:
            # Activate the logger after all callbacks have had a
            # chance to call result() or exception().
            if self._log_traceback and self._tb_logger is not None:
                self._tb_logger.activate()
        self._exc_info = exc_info


set_exception就是调用了set_exc_info。

一样也能够看出,set_exc_info函数的参数exc_info,是一个长度为3的元祖,(class, instance, information)。

这里的_log_traceback和,_tb_logger是用来防止有异常发生,若是没有人尝试获取,异常不会被正常抛出。

后面有详细的解释。


能够看到self.set_exc_info和self.set_result方法,都是更新相应的结果属性,而后调用回调函数。


接着看看如何从future中获取结果

def result(self, timeout=None):
        """If the operation succeeded, return its result.  If it failed,
        re-raise its exception.
        """
        self._clear_tb_log()
        if self._result is not None:
            return self._result
        if self._exc_info is not None:
            raise_exc_info(self._exc_info)
        self._check_done()
        return self._result

self._clear_tb_log方法是从新初始化self._log_traceback和self._tb_logger。

若是有结果,就返回_result。若是有异常,就抛出异常。

若是都没有,说明有两种状况。

  1. 异步的程序尚未完成,没有结果或者异常返回。

  2. 异步的程序已经完成,只不过返回的结果就是None

因此才会有self.check_done方法,检查。

def _check_done(self):
        if not self._done:
            raise Exception("DummyFuture does not support blocking for results")


若是是第一种,就会抛出异常。说明self.result方法不支持阻塞等待结果返回的。一样也说明,直接获取result,必须在保证future已经返回结果或异常了。否则,得须要调用done接口判断

def done(self):
        """Returns True if the future has finished running."""
        return self._done

若是是第二种,直接返回self._result


获取异常:

#获取异常
def exception(self, timeout=None):
        """If the operation raised an exception, return the `Exception`
        object.  Otherwise returns None.
        """
        self._clear_tb_log()
        if self._exc_info is not None:
            return self._exc_info[1]
        else:
            self._check_done()
            return None
            
#获取异常信息
def exc_info(self):
        """Returns a tuple in the same format as `sys.exc_info` or None.

        .. versionadded:: 4.0
        """
        self._clear_tb_log()
        return self._exc_info


exception一样不支持阻塞等待。不过exception的值是不可能为空的。因此,就存在两种状况。

  1.  self._exc_info不为空,返回异常实例

  2. self._exc_info为空, 说明要么异步没有完成,要么异步完成,成功返回结果。

def exc_info(self):
        """Returns a tuple in the same format as `sys.exc_info` or None.

        .. versionadded:: 4.0
        """
        self._clear_tb_log()
        return self._exc_info

exc_info方法直接返回self._exc_info。


最后再来看看_TracebackLogger的原理:

为首先看下这个状况,当future返回异常,但由于程序没有尝试获取异常,那么这个异常就会被默默的遗弃,那么怎么才能让这个异常让用户知道。

有人会说在__del__方法里,调用app_log.error记录错误信息。由于__del__方法,在对象被回收时,会自动调用。

可是在python3.4之前,若是对象与对象有着相互引用,那么它们的__del__方法,是不会被调用的。

也叫是说这个__del__方法不必定会被调用。那么,tornado又是怎样实现的呢?


每一个Future对象会只对应一个_TracebackLogger对象,它们之间是一对一的关系。当Future被回收时,——_TracebackLogger也会被回收,由于_TracebackLogger只会对应一个Future对象,因此_TracebackLogger的__del__方法,确定会被调用。


class _TracebackLogger(object):

    __slots__ = ('exc_info', 'formatted_tb')

    def __init__(self, exc_info):
        self.exc_info = exc_info
        self.formatted_tb = None

    def activate(self):
        exc_info = self.exc_info
        if exc_info is not None:
            self.exc_info = None
            self.formatted_tb = traceback.format_exception(*exc_info)

    def clear(self):
        self.exc_info = None
        self.formatted_tb = None

    def __del__(self):
        if self.formatted_tb:
            app_log.error('Future exception was never retrieved: %s',
                          ''.join(self.formatted_tb).rstrip())

很简单的几个方法,调用activate会更新self.formatted_tb属性。而后__del__方法会根据这个属性,判断是否记录log。

相关文章
相关标签/搜索
本站公众号
   欢迎关注本站公众号,获取更多信息