Python:Tornado 第一章:異步及協程基礎:第三節:協程

上一篇文章: 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關鍵字實現,因此不能像普通函數那樣直接調用。
協程函數能夠經過如下三張方式調用:異步

  • 在自己是協程的函數內經過yield關鍵字調用。
  • 在IOLoop還沒有啓動時,經過IOLoop的run_sync()函數調用。
  • 在IOLoop已經啓動時,經過IOLoop的spawn_callback()函數調用。
實例:經過協程函數調用協程函數

代碼:函數

#用協程技術開發網頁訪問功能
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

實例:IOLoo還沒有啓動時,經過IOLoop的run_sync()函數調用。
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】網站

實例:在IOLoop啓動時,經過spawn_callback()函數調用

代碼:

#用協程技術開發網頁訪問功能
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)
相關文章
相關標籤/搜索