Tornado 4.3ドキュメント翻訳:ユーザーズガイド-協程
訳者は
きょうてい
Tornadoでは、非同期コードを書くことを推奨する.コンシステントは、チェーンコールバックの代わりにPythonの
コパスの使用は同期コードを書くように簡単であり、追加のスレッドを無駄にする必要はない.また、コンテキストの切り替えを減らすことで、同時プログラミングをより簡単にすることができる.
例:
Python 3.5:
原生コンダクタンスは、特定のフレーム(例えば、装飾器を使用する、
どのように機能していますか
次は、コーディネータの内部ループの簡単なバージョンです.
デザイナは、ジェネレータから
コパスの呼び出し方法
コンシステントは一般に異常を放出することはない:それらが放出するいかなる異常も
ほとんどの場合、いずれの呼び出しコンシステントの関数もコンシステント自身である必要があり、呼び出し時に
時には協程に「一労永逸」し、その結果を待たないことを望んでいるかもしれません.この場合、
最後に、プログラムの最上位レベルで、
コンシステントモード
結合callback
ブロック関数の呼び出し
ブロック関数をコンシステントから呼び出す最も簡単な方法は、
パラレル
コプロセッサは、リストまたは辞書オブジェクトのそれぞれの
クロスアクセス
すぐにyieldよりも
ループ
Pythonでは
バックグラウンドで実行
より複雑なサイクルに遭遇することがあります.例えば、前回のサイクル運転では、
Tornado 4.3
は2015年11月6日に発表され、このバージョンはPython3.5
のasync
/await
キーワードを正式にサポートし、旧バージョンCPythonでTornadoをコンパイルしても同様にこの2つのキーワードを使用することができ、これは間違いなく進歩である.次に、これはPython2.6
とPython3.2
をサポートする最後のバージョンであり、後続のバージョンでは互換性が削除されます.今ネット上でまだTornado4.3
の中国語のドキュメントがなくて、だからもっと多くの友达にそれに接触して学ぶことができるように、私はこの翻訳プロジェクトを始めて、興味のある友达が一緒に翻訳に参加することができることを望んで、プロジェクトの住所はtornado-zh on Githubで、翻訳したドキュメントはRead the Docsの上で直接見ることができます.Issues or PRへようこそ.きょうてい
Tornadoでは、非同期コードを書くことを推奨する.コンシステントは、チェーンコールバックの代わりにPythonの
yield
キーワードを使用してプログラムを停止および復元する実行(geventに現れる軽量レベルのスレッド連携方式のように、コンシステントとも呼ばれる場合があるが、Tornadoにおけるすべてのコンシステントは明確なコンテキスト切替を使用し、非同期関数と呼ばれる).コパスの使用は同期コードを書くように簡単であり、追加のスレッドを無駄にする必要はない.また、コンテキストの切り替えを減らすことで、同時プログラミングをより簡単にすることができる.
例:
from tornado import gen
@gen.coroutine
def fetch_coroutine(url):
http_client = AsyncHTTPClient()
response = yield http_client.fetch(url)
# Python 3.3 , generator
# .
# raise gen.Return(response.body).
return response.body
Python 3.5:
async
とawait
Python 3.5は、async
およびawait
のキーワード(これらのキーワードを使用する関数を「原生協程」とも呼ぶ)を導入する.Tornado 4.3から、yield
をベースとしたコラボレーションの代わりに使用できます.単純にasync def foo()
を使用するだけで、関数定義時に@gen.coroutine
装飾器の代わりにawait
をyieldの代わりに使用します.このドキュメントの他のセクションでは、古いバージョンのPythonと互換性のあるyield
のスタイルを引き続き使用しますが、async
とawait
が使用可能であれば、より高速に実行できます.
async def fetch_coroutine(url):
http_client = AsyncHTTPClient()
response = await http_client.fetch(url)
return response.body
await
キーワードはyield
キーワードより機能が少ない.例えば、yield
を使用するスレッドでは、Futures
のリストを得ることができますが、原生スレッドでは、リストをtornado.gen.multi
で包む必要があります.tornado.gen.convert_yielded
を使用して、yield
を使用して動作するコードをawait
を使用する形式に変換することもできます.原生コンダクタンスは、特定のフレーム(例えば、装飾器を使用する、
tornado.gen.coroutine
またはasyncio.coroutine
など)に著しく依存するが、すべてのコンダクタンスが他のものと互換性があるわけではない.1つのcoroutine runnerは、最初のコパスが呼び出されたときに選択する、await
で直接呼び出されたすべてのコパスによって共有される.Tornadoのコーディネーター(coroutine runner)は設計上多用途であり、他のフレームワークからのawaitableオブジェクトを受け入れることができる.他のコンポジット実行時には多くの制限がある場合がある(例えば、asyncio
コンポジット実行者は他のフレームからのコンポジットを受け入れない).これらの理由から、複数のフレームワークを組み合わせたアプリケーションは、Tornadoのコパス実行者を用いてコパススケジューリングを行うことを推奨する.Tornadoを使用するasyncioを実行するためのコンセンサスをスケジューリングするために、tornado.platform.asyncio.to_asyncio_future
アダプタを使用することができる.どのように機能していますか
yield
のキーワードを含む関数はジェネレータである.すべてのジェネレータは非同期です.それらを呼び出すと、実行済みの結果ではなくジェネレータオブジェクトが返されます.@gen.coroutine
デコレラはyield
式を介してジェネレータと交流する、1つの.Future
を返すことによって、コモンの呼び出し元と対話する.次は、コーディネータの内部ループの簡単なバージョンです.
# tornado.gen.Runner
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)
デザイナは、ジェネレータから
Future
オブジェクトを受信、(ブロックされていない)このFuture
オブジェクトの実行が完了するのを待ってから、「unwraps」というFuture
オブジェクトを解き、結果をyield
式の結果としてジェネレータに返す.ほとんどの非同期コードはFuture
クラスに直接接触することはない.Future
が直ちに非同期関数を介してyield
式に戻る場合を除く.コパスの呼び出し方法
コンシステントは一般に異常を放出することはない:それらが放出するいかなる異常も
.Future
に捕獲され、それが得られるまで捕獲される.これは、正しい方法でスレッドを呼び出すことが重要であることを意味します.そうしないと、無視されるエラーが発生する可能性があります. @gen.coroutine
def divide(x, y):
return x / y
def bad_call():
# ZeroDivisionError ,
# .
divide(1, 0)
ほとんどの場合、いずれの呼び出しコンシステントの関数もコンシステント自身である必要があり、呼び出し時に
yield
キーワードを使用する.スーパークラスのメソッドを複写する場合は、ドキュメントを参照して、サポートされているかどうかを確認します(ドキュメントには、「1つのパスかもしれない」または「1つのFuture
クラスを返すかもしれない」と書かれているはずです). @gen.coroutine
def good_call():
# yield divide() Future
yield divide(1, 0)
時には協程に「一労永逸」し、その結果を待たないことを望んでいるかもしれません.この場合、
.IOLoop.spawn_callback
が呼び出しを担当するように.IOLoop
を使用することを提案する.失敗した場合、.IOLoop
はログに呼び出しスタックを記録します. # IOLoop , .
# ,
# IOLoop .
IOLoop.current().spawn_callback(divide, 1, 0)
最後に、プログラムの最上位レベルで、
.IOLoop
がまだ実行されていない場合は、.IOLoop
を起動し、コンセンサスを実行し、.IOLoop.run_sync
メソッドを使用して.IOLoop
を停止することができる.これは通常、バッチ向けのmain
関数を起動するために使用されます. # run_sync() , lambda .
IOLoop.current().run_sync(lambda: divide(1, 0))
コンシステントモード
結合callback
.Future
の代わりにコールバックを使用する非同期コードと対話するために、.Task
クラスに呼び出しパケットを格納.これにより、コールバックパラメータが追加され、yield可能な.Future
が返されます. @gen.coroutine
def call_task():
# some_function.
# Task
# some_function(other_args, callback=callback)
yield gen.Task(some_function, other_args)
ブロック関数の呼び出し
ブロック関数をコンシステントから呼び出す最も簡単な方法は、
concurrent.futures.ThreadPoolExecutor
を使用することです.これは、コンシステントと互換性のあるFutures
を返します. thread_pool = ThreadPoolExecutor(4)
@gen.coroutine
def call_blocking():
yield thread_pool.submit(blocking_func, args)
パラレル
コプロセッサは、リストまたは辞書オブジェクトのそれぞれの
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]
# HTTPResponses
@gen.coroutine
def parallel_fetch_dict(urls):
responses = yield {url: http_client.fetch(url)
for url in urls}
# {url: HTTPResponse}
クロスアクセス
すぐにyieldよりも
.Future
を保存する方が役に立つ場合がありますので、待つ前に他の操作を実行できます. @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()
ループ
Pythonでは
for
サイクルまたはwhile
サイクルyield
反復器でyieldの結果をキャプチャすることができないため、コヒーレントなサイクルは厄介である.逆に、ループ条件をアクセス結果から分離する必要があります.以下は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()
バックグラウンドで実行
PeriodicCallback
通常はコパスは使用する.逆に、1つのコプロセッサは、1つのwhile True:
ループを含み、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)
より複雑なサイクルに遭遇することがあります.例えば、前回のサイクル運転では、
60+N
秒がかかるが、N
はdo_something()
がかかる時間である.60秒ごとに正確に動作するには、上記のクロスモードを使用します. @gen.coroutine
def minute_loop2():
while True:
nxt = gen.sleep(60) # .
yield do_something() # .
yield nxt # .