基於Python產生器的Tornado協程非同步

來源:互聯網
上載者:User

Tornado 4.0 已經發布了很長一段時間了, 新版本廣泛的應用了協程(Future)特性. 我們目前已經將 Tornado 升級到最新版本, 而且也大量的使用協程特性.

很長時間沒有更新部落格, 今天就簡單介紹下 Tornado 協程實現原理, Tornado 的協程是基於 Python 的產生器實現的, 所以首先來回顧下產生器.

產生器

Python 的產生器可以儲存執行狀態 並在下次調用的時候恢複, 通過在函數體內使用 yield 關鍵字 來建立一個產生器, 通過內建函數 next 或產生器的 next 方法來恢複產生器的狀態.

 代碼如下 複製代碼

def test():
    yield 1

我們調用 test 函數, 此時並不會返回結果, 而是會返回一個產生器

 代碼如下 複製代碼
>>> test()
<generator object test at 0x100b3b320>

我們調用其 next 方法則返回 yield 關鍵字之後的內容.

 代碼如下 複製代碼

>>> t = test()
>>> t.next()

如果我們接著調用 next 方法, 後面又沒有 yield 關鍵字繼續返回的話, 會拋出一個 StopIteration 異常.

yield 關鍵字不僅僅能從產生器內部返回狀態, 同時也可以將外部資訊傳遞到產生器內部, 通過將 yeild 關鍵裡賦值給變數, 並調用產生器的 send 方法來將對象傳遞到產生器 內部. 需要注意的是產生器的開始必須調用其 next 方法, 後面 send 方法調用的同時 也會觸發 next 動作. 如果沒有變數接收 yield 關鍵字那麼 send 傳遞的值將會 被丟棄.

 代碼如下 複製代碼

>>> def test():
    a = yield
    print(a)

首先調用 next 上面函數返回的產生器將返回 None, 如果這時候直接調用 next 將 會給產生器發送 None, 如果調用 send 發送一個值, 將列印這個值並拋出 StopIteration 異常.

一個簡單地協程
以上就是實現協程的所有基礎, 為了加深理解, 我們這裡寫一個小例子, 例子我們只使用協程 開啟兩個甚至多個死迴圈, 下面就是一個極其簡單地例子::

 代碼如下 複製代碼

#!/usr/bin/env python
# -*- coding:utf-8 -*-

from __future__ import absolute_import, print_function, division, with_statement


def loop1():
    """ 迴圈1負責拋出一個函數和對應的參數, 並接收結果
    """
    a = 0
    ret = 1
    while True:
        ret = yield sum, [a, ret]
        a, ret = ret, a
        print("Loop1 ret", ret)


def loop2():
    """ 迴圈2 負責接收函數並計算結果, 然後 yield 出結果
    """
    while True:
        func, args = yield
        yield func(args)
        print("Loop2")


l1 = loop1()
l2 = loop2()
tmp = l1.next()

for i in range(10):
    l2.next()
    ret = l2.send(tmp)
    tmp = l1.send(ret)

上面例子裡 loop1 負責產生任務, loop2 負責執行任務, 主迴圈負責調度任務並將任務結果發回給 任務產生者.

Tornado 如何做的

我們首先看一個使用 Tornado 協程非同步例子

 代碼如下 複製代碼

#!/usr/bin/env python
# -*- coding:utf-8 -*-

from __future__ import absolute_import, print_function, division, with_statement

from tornado import gen
from tornado import web
from tornado import httpclient


class ActionHandler(web.RequestHandler):

    @gen.coroutine
    def get(self):
        response = yield httpclient.AsyncHTTPClient().fetch("http://www.111cn.net")

        # ...

其實原理在上面簡單地例子裡已經講清楚了, 我們來簡單分析一遍上面的例子, 首先 Tornado 得到 ActionHandler.get 方法拋出(next)的一個任務, 然後非同步去執行任務, 當任務(網路請求)結束或 異常時 Tornado 取得事件通知然後將結果放回(send)到該方法中讓該方法繼續執行.

由於是非同步, 調用這個方法並不會阻塞其他任務執行.

這時候我們的方法其實就是上個例子 loop1 函數, 而 Tornado 調度並執行了其拋出的任務.

總結

Tornado 的協程非同步可以讓非同步看起來是順序執行的, 可以從一大串的 callback 中解脫出來.

Tornado 的協程非同步並不是這三言兩語能說清楚的, 其中有很複雜的封裝和傳遞, 有興趣可以自己 閱讀源碼.

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.