tornado.concurrent-Future

Future对象经常用于异步,在gen.coroutine中实现异步代码同步化。了解Future,可以更好的处理返回的结果或异常。

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

?
1
2
3
4
def  async_task():
     future  =  Future()
     ioloop.IOLoop.current().call_later( lambda  f: f.set_result( 'hello' ))
     return  future

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

?
1
2
3
4
5
6
def  callback(future):
     try :
         result  =  future.result()
     except :
         print  "exception occured"
     print  "future return result: %s"  %  result


注册回调函数:

?
1
2
future  =  async_task()
future.set_done_callback(callback)



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

?
1
2
3
4
5
6
7
8
9
10
11
12
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),可以看出回调函数的格式。

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

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


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

?
1
2
3
4
5
6
7
8
    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方法。

?
1
2
3
4
5
6
7
8
9
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的回调函数。

最后跟新self._callbacks为None。


如果是返回异常:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
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中获取结果

?
1
2
3
4
5
6
7
8
9
10
11
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方法,检查。

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


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

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

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


获取异常:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#获取异常
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为空, 说明要么异步没有完成,要么异步完成,成功返回结果。

?
1
2
3
4
5
6
7
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__方法,肯定会被调用。


?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
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。


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值