tornado.gen.coroutine-協程

1.  yield的基本概念html

2.  Future的用法python

3.  ioloop的經常使用接口app

4. gen.coroutine的應用異步

5. gen.coroutine的源碼async

6. Runner類的實現函數


1. yield的基本概念oop

python中使用yield實現了生成器函數,一樣yield.send( )方法也實現了控制權在函數間的跳轉。fetch

關於yield比較詳細的介紹,能夠參考這篇博文http://www.cnblogs.com/coderzh/articles/1202040.html spa


2.Future的用法code

Future是用來在異步過程當中,存放結果的容器。它能夠在結果出來時,進行回調。

add_done_callback(self, fn): 添加回調函數fn,函數fn必須以future做爲參數。

set_result(self, result): 存放結果,此函數會執行回調函數。

set_exception(self, exception):存放異常,此函數會執行回調函數。


3. ioloop的經常使用接口

add_future(self, future, callback): 當future返回結果時,會將callback登記到ioloop中,由ioloop在下次輪詢時調用。

add_callback(self, callback, *args, **kwargs): 將callback登記到ioloop中,由ioloop在下次輪詢時調用。

add_timeout(self, deadline, callback, *args, **kwargs): 將callback登記到ioloop中,由ioloop在指定的deadline時間調用。

def call_at(self, when, callback, *args, **kwargs): 將callback登記到ioloop中,由ioloop在指定的when時間調用。

def call_later(self, delay, callback, *args, **kwargs): 將callback登記到ioloop中, 由ioloop在指定的延遲delay秒後調用。

add_handler(self, fd, handler, events): 將文件符fd的event事件和回調函數handler登記到ioloop中,當事件發生時,觸發。


4. gen.coroutine的應用

直接上官網的例子

普通的回調函數的方式:

class AsyncHandler(RequestHandler):
    @asynchronous
    def get(self):
        http_client = AsyncHTTPClient()
        http_client.fetch("http://example.com",
                          callback=self.on_fetch)

    def on_fetch(self, response):
        do_something_with_response(response)
        self.render("template.html")

同步的方式:

class GenAsyncHandler(RequestHandler):
    @gen.coroutine
    def get(self):
        http_client = AsyncHTTPClient()
        response = yield http_client.fetch("http://example.com")
        do_something_with_response(response)
        self.render("template.html")

能夠看出代碼明顯清晰,簡單多了。若是有深層次的回調,效果會更明顯。


5. gen.coroutine的源碼

def coroutine(func, replace_callback=True):
    return _make_coroutine_wrapper(func, replace_callback=True)

接着看_make_coroutine_wrapper的源碼:

def _make_coroutine_wrapper(func, replace_callback):
    @functools.wraps(func)
    def wrapper(*args, **kwargs):
        future = TracebackFuture()

        if replace_callback and 'callback' in kwargs:
            callback = kwargs.pop('callback')
            IOLoop.current().add_future(
                future, lambda future: callback(future.result()))

        try:
            result = func(*args, **kwargs)
        except (Return, StopIteration) as e:
            result = getattr(e, 'value', None)
        except Exception:
            future.set_exc_info(sys.exc_info())
            return future
        else:
            if isinstance(result, types.GeneratorType):
                # Inline the first iteration of Runner.run.  This lets us
                # avoid the cost of creating a Runner when the coroutine
                # never actually yields, which in turn allows us to
                # use "optional" coroutines in critical path code without
                # performance penalty for the synchronous case.
                try:
                    orig_stack_contexts = stack_context._state.contexts
                    yielded = next(result)
                    if stack_context._state.contexts is not orig_stack_contexts:
                        yielded = TracebackFuture()
                        yielded.set_exception(
                            stack_context.StackContextInconsistentError(
                                'stack_context inconsistency (probably caused '
                                'by yield within a "with StackContext" block)'))
                except (StopIteration, Return) as e:
                    future.set_result(getattr(e, 'value', None))
                except Exception:
                    future.set_exc_info(sys.exc_info())
                else:
                    Runner(result, future, yielded)
                try:
                    return future
                finally:                   
                    future = None
        future.set_result(result)
        return future
    return wrapper


這段代碼注意到,有幾個關鍵地方。

future = TracebackFuture()

TracebackFuture就是Future,二者是同一個類。這裏定義了future變量, 是用來後面增長callback用的。當函數執行完時,會將這個future返回。 


result = func(*args, **kwargs)

這裏執行被裝飾的函數,返回result。咱們知道有yield語句的函數,返回結果是一個生成器。


yielded = next(result)

返回yield結果, next(result)被要求返回Future,list, dict,或者YieldPoint類型,通常返回Future。


Runner(result, future, yielded)

實例化Runner,這步很重要。


return future

返回future。


6. Runner類的實現

class Runner(object):
    def __init__(self, gen, result_future, first_yielded):
        self.gen = gen
        self.result_future = result_future
        self.future = _null_future
        self.yield_point = None
        self.pending_callbacks = None
        self.results = None
        self.running = False
        self.finished = False
        self.had_exception = False
        self.io_loop = IOLoop.current()
        self.stack_context_deactivate = None
        if self.handle_yield(first_yielded):
            self.run()

__init__構造函數首先初始化參數,而後調用handle_yield方法。


由於first_yielded爲Future類型, 因此簡化下handle_yield的代碼。

def handle_yield(self, yielded):
        self.future = yielded
        if not self.future.done():
            self.io_loop.add_future(
                self.future, lambda f: self.run())
            return False
        return True

handle_yield方法,首先判斷yielded是否已經返回結果,若是沒有就調用io_loop.add_future方法。這樣當yielded返回結果時,就會回調self.run方法。若是已經返回,就直接執行self.run方法。


self.run方法會將控制權返回給被gen.coroutine的函數。

def run(self):
        """Starts or resumes the generator, running until it reaches a
        yield point that is not ready.
        """
        if self.running or self.finished:
            return
        try:
            self.running = True
            while True:
                future = self.future
                if not future.done():
                    return
                self.future = None
                try:
                    orig_stack_contexts = stack_context._state.contexts
                    try:
                        value = future.result()
                    except Exception:
                        self.had_exception = True
                        yielded = self.gen.throw(*sys.exc_info())
                    else:
                        yielded = self.gen.send(value)
                    if stack_context._state.contexts is not orig_stack_contexts:
                        self.gen.throw(
                            stack_context.StackContextInconsistentError(
                                'stack_context inconsistency (probably caused '
                                'by yield within a "with StackContext" block)'))
                except (StopIteration, Return) as e:
                    self.finished = True
                    self.future = _null_future
                    if self.pending_callbacks and not self.had_exception:
                        # If we ran cleanly without waiting on all callbacks
                        # raise an error (really more of a warning).  If we
                        # had an exception then some callbacks may have been
                        # orphaned, so skip the check in that case.
                        raise LeakedCallbackError(
                            "finished without waiting for callbacks %r" %
                            self.pending_callbacks)
                    self.result_future.set_result(getattr(e, 'value', None))
                    self.result_future = None
                    self._deactivate_stack_context()
                    return
                except Exception:
                    self.finished = True
                    self.future = _null_future
                    self.result_future.set_exc_info(sys.exc_info())
                    self.result_future = None
                    self._deactivate_stack_context()
                    return
                if not self.handle_yield(yielded):
                    return
        finally:
            self.running = False


這裏注意到幾個關鍵地方。


value = future.result()

獲取yielded的結果值。


yielded = self.gen.send(value)

使用send方法將結果返回,而且將執行權交給被裝飾的函數。而且返回下一個yield的值。


若是函數生成器後面沒有yield語句了,就會拋出StopIteration異常。

調用set_result存儲結果,返回。

self.result_future.set_result(getattr(e, 'value', None))


若是函數生成器後面還有yield語句,就會調用handle_yield,仍然登記self.run方法。

if not self.handle_yield(yielded):
                    return

之因此要有個判斷,是須要在while循環裏,若是future已經有返回結果了,就繼續取結果,send結果,

一直到future的結果沒有返回或者沒有yield語句了。

相關文章
相關標籤/搜索