介紹Python的Tornado框架中的協程異步實現原理
這篇文章主要介紹了簡單介紹Python的Tornado框架中的協程異步實現原理,作者基於Python的生成器講述了Tornado異步的特點,需要的朋友可以參考下
Tornado 4.0 已經發布了很長一段時間了, 新版本廣泛的應用了協程(Future)特性. 我們目前已經將 Tornado 升級到最新版本, 而且也大量的使用協程特性.
很長時間沒有更新博客, 今天就簡單介紹下 Tornado 協程實現原理, Tornado 的協程是基於 Python 的生成器實現的, 所以首先來回顧下生成器.
生成器
Python 的生成器可以保存執行狀態 並在下次調用的時候恢復, 通過在函數體內使用 yield 關鍵字 來創建一個生成器, 通過內置函數 next 或生成器的 next 方法來恢復生成器的狀態.
?
1 2 def test(): yield 1我們調用 test 函數, 此時並不會返回結果, 而是會返回一個生成器
?
1 2 >>> test() <generator object test at 0x100b3b320>我們調用其 next 方法則返回 yield 關鍵字之後的內容.
?
1 2 3 >>> t = test() >>> t.next() 1如果我們接著調用 next 方法, 後面又沒有 yield 關鍵字繼續返回的話, 會拋出一個 StopIteration 異常.
yield 關鍵字不僅僅能從生成器內部返回狀態, 同時也可以將外部信息傳遞到生成器內部, 通過將 yeild 關鍵裡賦值給變量, 並調用生成器的 send 方法來將對象傳遞到生成器 內部. 需要注意的是生成器的開始必須調用其 next 方法, 後面 send 方法調用的同時 也會觸發 next 動作. 如果沒有變量接收 yield 關鍵字那麼 send 傳遞的值將會 被丟棄.
?
1 2 3 >>> def test(): a = yield print(a)首先調用 next 上面函數返回的生成器將返回 None, 如果這時候直接調用 next 將 會給生成器發送 None, 如果調用 send 發送一個值, 將打印這個值並拋出 StopIteration 異常.
一個簡單地協程
以上就是實現協程的所有基礎, 為了加深理解, 我們這裡寫一個小例子, 例子我們只使用協程 開啟兩個甚至多個死循環, 下面就是一個極其簡單地例子::
?
1 2 #!/usr/bin/env python # -*- coding:utf-8 -*-?
1 2 3 4 5 6 7 8 9 10 11 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)?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 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 協程異步的例子
?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 #!/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.linuxzen.com") # ...其實原理在上面簡單地例子裡已經講清楚了, 我們來簡單分析一遍上面的例子, 首先 Tornado 得到 ActionHandler.get 方法拋出(next)的一個任務, 然後異步的去執行任務, 當任務(網絡請求)結束或 異常時 Tornado 取得事件通知然後將結果放回(send)到該方法中讓該方法繼續執行.
由於是異步的, 調用這個方法並不會阻塞其他任務執行.
這時候我們的方法其實就是上個例子 loop1 函數, 而 Tornado 調度並執行了其拋出的任務.
總結
Tornado 的協程異步可以讓異步看起來是順序執行的, 可以從一大串的 callback 中解脫出來.
Tornado 的協程異步並不是這三言兩語能說清楚的, 其中有很復雜的封裝和傳遞, 有興趣可以自己 閱讀源碼.