成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專欄INFORMATION COLUMN

Tornado 4.3文檔翻譯: 用戶指南-協(xié)程

SimonMa / 1978人閱讀

摘要:譯者說于年月日發(fā)布,該版本正式支持的關(guān)鍵字,并且用舊版本編譯同樣可以使用這兩個(gè)關(guān)鍵字,這無(wú)疑是一種進(jìn)步。其次,這是最后一個(gè)支持和的版本了,在后續(xù)的版本了會(huì)移除對(duì)它們的兼容。

譯者說

Tornado 4.3于2015年11月6日發(fā)布,該版本正式支持Python3.5async/await關(guān)鍵字,并且用舊版本CPython編譯Tornado同樣可以使用這兩個(gè)關(guān)鍵字,這無(wú)疑是一種進(jìn)步。其次,這是最后一個(gè)支持Python2.6Python3.2的版本了,在后續(xù)的版本了會(huì)移除對(duì)它們的兼容。現(xiàn)在網(wǎng)絡(luò)上還沒有Tornado4.3的中文文檔,所以為了讓更多的朋友能接觸并學(xué)習(xí)到它,我開始了這個(gè)翻譯項(xiàng)目,希望感興趣的小伙伴可以一起參與翻譯,項(xiàng)目地址是tornado-zh on Github,翻譯好的文檔在Read the Docs上直接可以看到。歡迎Issues or PR。

協(xié)程

Tornado中推薦使用協(xié)程寫異步代碼. 協(xié)程使用了Python的yield關(guān)鍵字代替鏈?zhǔn)交卣{(diào)來(lái)將程序掛起和恢復(fù)執(zhí)行(像在 gevent中出現(xiàn)的輕量級(jí)線程合作方式有時(shí)也被稱為協(xié)程,但是在Tornado中所有的協(xié)程使用明確的上下文切換,并被稱為異步函數(shù)).

使用協(xié)程幾乎像寫同步代碼一樣簡(jiǎn)單, 并且不需要浪費(fèi)額外的線程. 它們還通過減少上下文切換來(lái) 使并發(fā)編程更簡(jiǎn)單.

例子:

    from tornado import gen

    @gen.coroutine
    def fetch_coroutine(url):
        http_client = AsyncHTTPClient()
        response = yield http_client.fetch(url)
        # 在Python 3.3之前, 在generator中是不允許有返回值的
        # 必須通過拋出異常來(lái)代替.
        # 就像 raise gen.Return(response.body).
        return response.body
Python 3.5:asyncawait

Python 3.5引入了asyncawait關(guān)鍵字(使用這些關(guān)鍵字的函數(shù)也被稱為"原生協(xié)程").從Tornado 4.3,你可以用它們代替yield為基礎(chǔ)的協(xié)程.只需要簡(jiǎn)單的使用async def foo()在函數(shù)定義的時(shí)候代替@gen.coroutine裝飾器,用await代替yield. 本文檔的其他部分會(huì)繼續(xù)使用yield的風(fēng)格來(lái)和舊版本的Python兼容, 但是如果asyncawait可用的話,它們運(yùn)行起來(lái)會(huì)更快:

    async def fetch_coroutine(url):
        http_client = AsyncHTTPClient()
        response = await http_client.fetch(url)
        return response.body

await關(guān)鍵字比yield 關(guān)鍵字功能要少一些.例如,在一個(gè)使用 yield 的協(xié)程中,你可以得到Futures 列表, 但是在原生協(xié)程中,你必須把列表用 tornado.gen.multi 包起來(lái). 你也可以使用 tornado.gen.convert_yielded來(lái)把任何使用yield工作的代碼轉(zhuǎn)換成使用await的形式.

雖然原生協(xié)程沒有明顯依賴于特定框架(例如它們沒有使用裝飾器,例如tornado.gen.coroutineasyncio.coroutine), 不是所有的協(xié)程都和其他的兼容. 有一個(gè)coroutine runner在第一個(gè)協(xié)程被調(diào)用的時(shí)候進(jìn)行選擇, 然后被所有用await直接調(diào)用的協(xié)程共享. Tornado的協(xié)程執(zhí)行者(coroutine runner)在設(shè)計(jì)上是多用途的,可以接受任何來(lái)自其他框架的awaitable對(duì)象;其他的協(xié)程運(yùn)行時(shí)可能有很多限制(例如,asyncio協(xié)程執(zhí)行者不接受來(lái)自其他框架的協(xié)程).基于這些原因,我們推薦組合了多個(gè)框架的應(yīng)用都使用Tornado的協(xié)程執(zhí)行者來(lái)進(jìn)行協(xié)程調(diào)度.為了能使用Tornado來(lái)調(diào)度執(zhí)行asyncio的協(xié)程, 可以使用tornado.platform.asyncio.to_asyncio_future適配器.

它是如何工作的

包含了yield關(guān)鍵字的函數(shù)是一個(gè)生成器(generator).所有的生成器都是異步的;當(dāng)調(diào)用它們的時(shí)候,會(huì)返回一個(gè)生成器對(duì)象,而不是一個(gè)執(zhí)行完的結(jié)果.@gen.coroutine裝飾器通過yield表達(dá)式和生成器進(jìn)行交流, 而且通過返回一個(gè).Future與協(xié)程的調(diào)用方進(jìn)行交互.

下面是一個(gè)協(xié)程裝飾器內(nèi)部循環(huán)的簡(jiǎn)單版本:

    # tornado.gen.Runner 簡(jiǎn)化的內(nèi)部循環(huán)
    def run(self):
        # send(x) makes the current yield return x.
        # It returns when the next yield is reached
        future = self.gen.send(self.next)
        def callback(f):
            self.next = f.result()
            self.run()
        future.add_done_callback(callback)

裝飾器從生成器接收一個(gè)Future對(duì)象, 等待(非阻塞的)這個(gè)Future對(duì)象執(zhí)行完成, 然后"解開(unwraps)"這個(gè)Future對(duì)象,并把結(jié)果作為yield 表達(dá)式的結(jié)果傳回給生成器.大多數(shù)異步代碼從來(lái)不會(huì)直接接觸Future類.除非 Future立即通過異步函數(shù)返回給yield表達(dá)式.

如何調(diào)用協(xié)程

協(xié)程一般不會(huì)拋出異常: 它們拋出的任何異常將被.Future捕獲直到它被得到.這意味著用正確的方式調(diào)用協(xié)程是重要的, 否則你可能有被忽略的錯(cuò)誤:

    @gen.coroutine
    def divide(x, y):
        return x / y

    def bad_call():
        # 這里應(yīng)該拋出一個(gè) ZeroDivisionError 的異常, 但事實(shí)上并沒有
        # 因?yàn)閰f(xié)程的調(diào)用方式是錯(cuò)誤的.
        divide(1, 0)

幾乎所有的情況下, 任何一個(gè)調(diào)用協(xié)程的函數(shù)都必須是協(xié)程它自身, 并且在調(diào)用的時(shí)候使用yield關(guān)鍵字. 當(dāng)你復(fù)寫超類中的方法, 請(qǐng)參閱文檔,看看協(xié)程是否支持(文檔應(yīng)該會(huì)寫該方法"可能是一個(gè)協(xié)程"或者"可能返回一個(gè) Future類 "):

    @gen.coroutine
    def good_call():
        # yield 將會(huì)解開 divide() 返回的 Future 并且拋出異常
        yield divide(1, 0)

有時(shí)你可能想要對(duì)一個(gè)協(xié)程"一勞永逸"而且不等待它的結(jié)果. 在這種情況下,建議使用.IOLoop.spawn_callback, 它使得.IOLoop 負(fù)責(zé)調(diào)用. 如果它失敗了, .IOLoop會(huì)在日志中把調(diào)用棧記錄下來(lái):

    # IOLoop 將會(huì)捕獲異常,并且在日志中打印棧記錄.
    # 注意這不像是一個(gè)正常的調(diào)用, 因?yàn)槲覀兪峭ㄟ^
    # IOLoop 調(diào)用的這個(gè)函數(shù).
    IOLoop.current().spawn_callback(divide, 1, 0)

最后, 在程序頂層, 如果.IOLoop尚未運(yùn)行, 你可以啟動(dòng).IOLoop,執(zhí)行協(xié)程,然后使用.IOLoop.run_sync方法停止.IOLoop. 這通常被用來(lái)啟動(dòng)面向批處理程序的main函數(shù):

    # run_sync() 不接收參數(shù),所以我們必須把調(diào)用包在lambda函數(shù)中.
    IOLoop.current().run_sync(lambda: divide(1, 0))
協(xié)程模式 結(jié)合 callback

為了使用回調(diào)代替.Future與異步代碼進(jìn)行交互, 把調(diào)用包在.Task類中. 這將為你添加一個(gè)回調(diào)參數(shù)并且返回一個(gè)可以yield的.Future :

    @gen.coroutine
    def call_task():
        # 注意這里沒有傳進(jìn)來(lái)some_function.
        # 這里會(huì)被Task翻譯成
        #   some_function(other_args, callback=callback)
        yield gen.Task(some_function, other_args)
調(diào)用阻塞函數(shù)

從協(xié)程調(diào)用阻塞函數(shù)最簡(jiǎn)單的方式是使用concurrent.futures.ThreadPoolExecutor, 它將返回和協(xié)程兼容的Futures:

    thread_pool = ThreadPoolExecutor(4)

    @gen.coroutine
    def call_blocking():
        yield thread_pool.submit(blocking_func, args)
并行

協(xié)程裝飾器能識(shí)別列表或者字典對(duì)象中各自的 Futures, 并且并行的等待這些 Futures :

    @gen.coroutine
    def parallel_fetch(url1, url2):
        resp1, resp2 = yield [http_client.fetch(url1),
                              http_client.fetch(url2)]

    @gen.coroutine
    def parallel_fetch_many(urls):
        responses = yield [http_client.fetch(url) for url in urls]
        # 響應(yīng)是和HTTPResponses相同順序的列表

    @gen.coroutine
    def parallel_fetch_dict(urls):
        responses = yield {url: http_client.fetch(url)
                            for url in urls}
        # 響應(yīng)是一個(gè)字典 {url: HTTPResponse}
交叉存取

有時(shí)候保存一個(gè) .Future 比立即yield它更有用, 所以你可以在等待之前
執(zhí)行其他操作:

    @gen.coroutine
    def get(self):
        fetch_future = self.fetch_next_chunk()
        while True:
            chunk = yield fetch_future
            if chunk is None: break
            self.write(chunk)
            fetch_future = self.fetch_next_chunk()
            yield self.flush()
循環(huán)

協(xié)程的循環(huán)是棘手的, 因?yàn)樵赑ython中沒有辦法在for循環(huán)或者while循環(huán)yield迭代器,并且捕獲yield的結(jié)果. 相反,你需要將循環(huán)條件從訪問結(jié)果中分離出來(lái), 下面是一個(gè)使用Motor的例子:

    import motor
    db = motor.MotorClient().test

    @gen.coroutine
    def loop_example(collection):
        cursor = db.collection.find()
        while (yield cursor.fetch_next):
            doc = cursor.next_object()
在后臺(tái)運(yùn)行

PeriodicCallback通常不使用協(xié)程. 相反,一個(gè)協(xié)程可以包含一個(gè)while True:循環(huán)并使用tornado.gen.sleep:

    @gen.coroutine
    def minute_loop():
        while True:
            yield do_something()
            yield gen.sleep(60)

    # Coroutines that loop forever are generally started with
    # spawn_callback().
    IOLoop.current().spawn_callback(minute_loop)

有時(shí)可能會(huì)遇到一個(gè)更復(fù)雜的循環(huán). 例如, 上一個(gè)循環(huán)運(yùn)行每次花費(fèi)60+N秒,其中Ndo_something()花費(fèi)的時(shí)間. 為了準(zhǔn)確的每60秒運(yùn)行,使用上面的交叉模式:

    @gen.coroutine
    def minute_loop2():
        while True:
            nxt = gen.sleep(60)   # 開始計(jì)時(shí).
            yield do_something()  # 計(jì)時(shí)后運(yùn)行.
            yield nxt             # 等待計(jì)時(shí)結(jié)束.

文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/37707.html

相關(guān)文章

  • Tornado 4.3文檔翻譯: 用戶指南-異步和非阻塞I/O

    摘要:譯者說于年月日發(fā)布,該版本正式支持的關(guān)鍵字,并且用舊版本編譯同樣可以使用這兩個(gè)關(guān)鍵字,這無(wú)疑是一種進(jìn)步。其次,這是最后一個(gè)支持和的版本了,在后續(xù)的版本了會(huì)移除對(duì)它們的兼容。 譯者說 Tornado 4.3于2015年11月6日發(fā)布,該版本正式支持Python3.5的async/await關(guān)鍵字,并且用舊版本CPython編譯Tornado同樣可以使用這兩個(gè)關(guān)鍵字,這無(wú)疑是一種進(jìn)步。其次...

    smartlion 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

最新活動(dòng)
閱讀需要支付1元查看
<