web-dev-qa-db-ja.com

同期v / s非同期

竜巻のドキュメントの紹介ページに記載されている基本的な例を理解しようとしています。 2ブロックのコードがあります。同期のものは私にとっては問題ありません、そして私はそれを理解しています。しかし、非同期のものは私が理解できないものです。

同期

from tornado.httpclient import HTTPClient

def synchronous_fetch(url):
    http_client = HTTPClient()
    response = http_client.fetch(url)
    return response.body

非同期

from tornado.httpclient import AsyncHTTPClient

def asynchronous_fetch(url, callback):
    http_client = AsyncHTTPClient()
    def handle_response(response):
        callback(response.body)
    http_client.fetch(url, callback=handle_response)

より良い例を提供できる場合は、そうしてください。

12
phraniiac

非同期呼び出しの考え方は、多くのWeb関連プログラミングでほぼ同じように機能します... "stuff"(frameworks 、サーバー、ライブラリ...)TornadoWebサーバーの概念だけではありません。

基本的な考え方は次のとおりです。

  • S̲y̲n̲c̲h̲r̲o̲n̲o̲u̲s̲リクエストでは、HTTPサーバーからの応答(またはサーバーに到達できない場合はエラー、サーバーに到達できない場合はタイムアウト)が発生するまで、リクエストを作成してプログラムの実行を停止します。応答)インタプリタは、リクエストが完了するまでブロックされます(リクエストで何が起こったのかについて明確な回答が得られるまで:うまくいきましたか?エラーがありましたか?タイムアウト? ...)。
  • A̲s̲y̲n̲c̲h̲r̲o̲n̲o̲u̲s̲リクエストでは、リクエストを「起動」、一種の「忘れる」、つまり、インタプリタは続行しますリクエストが完了した後、リクエストが完了するのを待たずにコードを実行します。

    これは...かなり無意味なようですよね?リクエストを「スペースのない場所に」送信し、通常どおり実行を続けますか?サーバーが応答を送信するとどうなりますか?リクエストしたのですが、どうしたのか知りたいです!そうでなければ、そもそもコードにそれを入力していなかっただろう!

    さて、ここでcallbackが入ります。リクエスト "をスペースのない場所に"[〜#〜 ] but [〜#〜]コールバック関数を提供するので、相手側のHTTPサーバーが応答を送信すると、その関数は最初に上記のresponseを使用して実行されます。引数。

Enの例でそれを見てみましょう。

ハンドラーが1つだけの非常に単純なTornadoサーバー(Python 2.7Tornado 4.2を使用)を作成しました。 GETでは、戻るのに5秒かかります。 time.sleep でそれを実行しましたが、実際には、非常に時間のかかるプロセスになる可能性があります(データベースにアクセスし、計算を実行します...誰が知っていますか?...)

サーバーファイルは次のとおりです(Tornadoのドキュメントで提供されている に基づく):

SampleServer.py

#!/usr/bin/env python2.7
import time
import tornado.ioloop
import tornado.web


class MainHandler(tornado.web.RequestHandler):
    def get(self):
        print "Someone is GET'ing me"
        time.sleep(5)
        self.write("Hello, world")

application = tornado.web.Application([
        (r"/", MainHandler),
    ])

if __name__ == "__main__":
    application.listen(8888)
    print "Starting sample server."
    tornado.ioloop.IOLoop.current().start()

ターミナルを開き、そのコードを実行してサーバーを作成します。ローカルマシンのポート8888でトルネードがリッスンします。

次に、2つの方法でGETs http://localhost:8888する別のスクリプト(別の端末で実行する必要があります)を作成しましょう。最初は同期、次に非同期です。

SampleFetcher.py

#!/usr/bin/env python2.7
import datetime
import tornado.ioloop
from tornado.httpclient import HTTPClient, AsyncHTTPClient


def HUMAN_DT_NOW():
    return datetime.datetime.now().strftime("%Y/%m/%d %H:%M:%S")


def synchronous_fetch(url):
    http_client = HTTPClient()
    start_dt = datetime.datetime.now()
    response = http_client.fetch(url)
    end_dt = datetime.datetime.now()
    print ("The synchronous fetch took %s seconds."
           % (end_dt - start_dt).total_seconds())
    print "(Sync) Server said: \"%s\"" % response.body


def asynchronous_fetch(url):
    http_client = AsyncHTTPClient()

    def handle_response(response):
        print ""
        print "Yawwza... Finally!!!."
        print "The time now is %s" % HUMAN_DT_NOW()
        print "(Async) Server said: \"%s\"" % response.body
    print "Gonna launch a 'fetch' to the universe at %s..." % HUMAN_DT_NOW()
    http_client.fetch(url, callback=handle_response)

if __name__ == "__main__":
    print " ------ Synchronous ------ "
    print ("Starting synchronous fetch at %s."
           " The program will block for about 5 secs." % HUMAN_DT_NOW())
    synchronous_fetch('http://localhost:8888')
    print "Pfew! That was a lot of wait time!!. I got bored watching my terminal"
    print ""
    print "Aight, let's see what Asynchronous can do"
    print " ------ Asynchronous ------ "
    asynchronous_fetch('http://localhost:8888')
    print "You're gonna see this line before the \"Yawwza...\" one"
    print "This one too. Now is %s" % HUMAN_DT_NOW()
    # The IOLoop below is required to prevent the script from closing ahead
    # of time, but allowing Asynchronous interactions
    tornado.ioloop.IOLoop.current().start()

これは出力します:

Starting synchronous fetch at 2015/07/04 13:25:47. The program will block for about 5 secs.
The synchronous fetch took 5.009597 seconds.
(Sync) Server said: "Hello, world"
Pfew! That was a lot of wait time!!. I got bored watching my terminal

Aight, let's see what Asynchronous can do
 ------ Asynchronous ------ 
Gonna launch a 'fetch' to the universe at 2015/07/04 13:25:52...
You're gonna see this line before the "Yawwza..." one
This one too. Now is 2015/07/04 13:25:52

Yawwza... Finally!!!.
The time now is 2015/07/04 13:25:57
(Async) Server said: "Hello, world"

ここでは、非同期部分に焦点を当てましょう。

 ------ Asynchronous ------ 
Gonna launch a 'fetch' to the universe at 2015/07/04 13:25:52...
You're gonna see this line before the "Yawwza..." one
This one too. Now is 2015/07/04 13:25:52

Yawwza... Finally!!!.
The time now is 2015/07/04 13:25:57
(Async) Server said: "Hello, world"

ご覧のとおり、スクリプトはasynchronous_fetch13:25:52で作成しましたが、すぐに(同じ秒で)、インタプリタは続行しました実行し、要求が行われた後に次のステートメントを実行します(You're gonna see this line before the "Yawwza..." oneおよびThis one too. Now is 2015/07/04 13:25:52を出力する行)。

次に、約5秒後、サーバーが応答し、callback関数(handle_responseでした)が実行されました。

Yawwza... Finally!!!.
The time now is 2015/07/04 13:25:57

これがアイデアの理解に少し役立つことを願っています。これは非常に便利な概念であり、トルネードだけに適用されるわけではありません。

提供されている2つのサンプルスクリプトを自由に試して、変更を加え、サーバーが応答するまでの時間を増やしてください...

さらに推奨される読み物:

33
BorrajaX