上一篇文章: Python:Tornado 第一章:異步及協程基礎:第二節:Python關鍵字yield
下一篇文章: Python:Tornado 第二章:實戰演練:開發Tornado網站:第一節:網站結構:HelloWorld
使用Tornado協程能夠開發出相似同步代碼的異步行爲。同時,由於協程自己不使用線程,因此減小了線程上下文切換的開銷,是一種高效的開發模式。編程
實例:用協程技術開發網頁訪問功能segmentfault
#用協程技術開發網頁訪問功能 from tornado import gen #引入協程庫gen from tornado.httpclient import AsyncHTTPClient import time #使用gen.coroutine修飾器 @gen.coroutine def coroutine_visit(): http_client=AsyncHTTPClient() response=yield http_client.fetch("http://www.baidu.com") print(response.body)
本例中任然使用了異步客戶端AsyncHTTPClient進行頁面訪問,裝飾器@gen.coroutine聲明這是一個協程函數,因爲yield關鍵字的做用,使得代碼中不用再編寫回調函數用於處理訪問結果,而能夠直接在yield語句的後面編寫結果處理語句。api
因爲Tornado協程基於Python的yield關鍵字實現,因此不能像普通函數那樣直接調用。
協程函數能夠經過如下三張方式調用:異步
代碼:函數
#用協程技術開發網頁訪問功能 from tornado import gen #引入協程庫gen from tornado.httpclient import AsyncHTTPClient import time #使用gen.coroutine修飾器 @gen.coroutine def coroutine_visit(): http_client=AsyncHTTPClient() response=yield http_client.fetch("http://www.baidu.com") print(response.body) @gen.coroutine def outer_coroutine(): print("start call coroutine_visit") yield coroutine_visit() print("end call coroutine_cisit")
本例中outer_coroutine()和coroutine_visit()都是協程函數,因此他們之間能夠經過yield關鍵字調用。_tornado
IOLoop是Tornado的主事件循環對象,Tornado程序經過它監聽外部客戶端的訪問請求,並執行相應操做。
代碼:oop
#用協程技術開發網頁訪問功能 from tornado import gen #引入協程庫gen from tornado.httpclient import AsyncHTTPClient from tornado.ioloop import IOLoop #引入IOLoop對象 #使用gen.coroutine修飾器 @gen.coroutine def coroutine_visit(): http_client=AsyncHTTPClient() response=yield http_client.fetch("http://www.baidu.com") print(response.body) def func_normal(): print("start call coroutine_visit") IOLoop.current().run_sync(lambda :coroutine_visit()) print("end call coroutine_visit")
當程序還沒有進入IOLoop的running狀態時,能夠經過run_sync()函數調用協程函數。⚠️注意:run_sync()函數將阻塞當前函數的調用,直到被調用的協程執行完成。性能
事實上,Tornado要求協程函數在IOLoop的running狀態種才能被調用,只不過run_sync函數自動完成了啓動、中止IOLoop的操做步驟,他的實現邏輯是:fetch
【啓動IOLoop】》【調用被lambda封裝的協程函數】》【中止IOLoop】網站
代碼:
#用協程技術開發網頁訪問功能 from tornado import gen #引入協程庫gen from tornado.httpclient import AsyncHTTPClient from tornado.ioloop import IOLoop #引入IOLoop對象 #使用gen.coroutine修飾器 @gen.coroutine def coroutine_visit(): http_client=AsyncHTTPClient() response=yield http_client.fetch("http://www.baidu.com") print(response.body) def func_normal(): print("start call coroutine_visit") IOLoop.current().spawn_callback(coroutine_visit) print("end call coroutine_visit")
spawn_callback()函數將不會等待被調用協程執行完成,全部上下兩條打印語句將立刻完成,而coroutine__visit自己將會由IOLoop在合適的時機進行調用。⚠️注意:IOLoop的spawn_callback()函數沒有爲開發者提供獲取協程函數調用返回值的方法,因此只能用span_callback()調用沒有返回值的協程函數。
在協程中直接調用阻塞函數會影響協程自己的性能,因此Tornado提供了在協程中利用線程池調度阻塞函數,從而不影響協程自己繼續執行的方法。
代碼實例:
from concurrent.futures import ThreadPoolExecutor from tornado import gen #定義線程池 thread_pool=ThreadPoolExecutor(2) def mySleep(count): import time for x in range(count): time.sleep(1) @gen.coroutine def call_blocking(): print("start") yield thread_pool.submit(mySleep,10) print("end")
代碼中首先引用了concurrent.futures種的ThreadPoolExecutor類,實例化了一個由兩個線程的線程池thread_pool。在須要調用阻塞函數的協程call_blocking種,使用thread_pool.submit調用阻塞函數,並經過yield返回。這樣便不會阻塞協程所在的線程的繼續執行,也保證了阻塞函數先後代碼的執行順序。
到目前爲止,咱們知道了協程中一個yield關鍵字等待一個異步調用的編程方法。其實,Tornado容許在協程中用一個yield關鍵字等待多個異步調用,只須要把這些調用以列表(list)或字典(dictionary)的方式傳遞給yield關鍵字便可。
#使用列表方式傳遞多個異步調用 from tornado import gen #引入協程庫gen from tornado.httpclient import AsyncHTTPClient @gen.coroutine #使用gen.coroutine修飾器 def coroutine_visit(): http_client=AsyncHTTPClient() list_response=yield [ http_client.fetch("http://www.baidu.com"), http_client.fetch("http://www.api.jiutouxiang.com") ] for response in list_response: print(response.body)
在代碼中仍然使用@gen.coroutine裝飾器定義協程,在須要yield的地方用列表傳遞若干個異步調用,只有在列表種的全部調用都執行完成後,yield纔會返回而且繼續執行。yield以列表方式返回調用結果。
#使用列表方式傳遞多個異步調用 from tornado import gen #引入協程庫gen from tornado.httpclient import AsyncHTTPClient @gen.coroutine #使用gen.coroutine修飾器 def coroutine_visit(): http_client=AsyncHTTPClient() dict_response=yield { "baidu": http_client.fetch("http://www.baidu.com"), "9siliao":http_client.fetch("http://www.api.jiutouxiang.com") } print(dict_response["baidu"].body)