4

Tornado のチャット デモでは、次のようなメソッドがあります。

@tornado.web.asynchronous
def post(self):
    cursor = self.get_argument("cursor", None)
    global_message_buffer.wait_for_messages(self.on_new_messages,
                                            cursor=cursor)

私はこの長いポーリングにかなり慣れていないため、スレッド化がどのように機能するかを正確には理解していませんが、次のように述べています。

ノンブロッキング ネットワーク I/O を使用することで、Tornado は数万のオープン接続に拡張できます...

私の理論は、単純なアプリを作成することで、次のようになりました。

import tornado.ioloop
import tornado.web
import time

class MainHandler(tornado.web.RequestHandler):
    @tornado.web.asynchronous
    def get(self):
        print("Start request")
        time.sleep(4)
        print("Okay done now")
        self.write("Howdy howdy howdy")
        self.finish()

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

2 つのリクエストを連続して行った場合 (つまり、2 つのブラウザ ウィンドウを開き、両方をすばやく更新した場合)、次のように表示されます。

Start request
Start request
Okay done now
Okay done now

代わりに、私は見る

Start request
Okay done now
Start request
Okay done now

これにより、この場合、実際にはブロックされていると思います。私のコードがブロックされているのはなぜですか? また、コードに期待どおりの動作をさせるにはどうすればよいですか? コアi7を搭載したWindows 7と、2つのコアを搭載したLinux Mint 13ボックスで同じ出力が得られます。

編集:

私は1つの方法を見つけました-誰かがクロスプラットフォームで機能する方法を提供できる場合(私はパフォーマンスについてあまり心配していませんが、それがノンブロッキングであることだけを心配しています)、私はその答えを受け入れます.

4

3 に答える 3

6

元の問題のコードの問題は、呼び出すとtime.sleep(4)、イベントループの実行が 4 秒間効果的にブロックされることです。そして、受け入れられた答えも問題を解決しません(IMHO)。

Tornado での非同期サービングは信頼に基づいて機能します。Tornado は何かが起こるたびに関数を呼び出しますが、できるだけ早く制御を戻すことを信頼しています。でブロックするとtime.sleep()、この信頼が破られます - Tornado は新しい接続を処理できません。

複数のスレッドを使用しても、間違いが隠されるだけです。数千のスレッドで Tornado を実行すると (数千の接続を同時に処理できるようになります)、非常に非効率的です。適切な方法は、Tornado 内でのみブロックする単一のスレッドを実行することです (selectまたは Tornado のイベントをリッスンする方法が何であれ) - コードではありません (正確には、コードではありません)。

適切な解決策は、次のように(を呼び出さずに)get(self)直前から戻ることです。time.sleep()self.finish()

class MainHandler(tornado.web.RequestHandler):
    @tornado.web.asynchronous
    def get(self):
        print("Starting")

もちろん、このリクエストはまだ開いていることを覚えておく必要がありwrite()ますfinish()

chat demoをご覧になることをお勧めします。認証を削除すると、非同期ロング ポーリング サーバーの非常に優れた例が得られます。

于 2013-05-17T07:28:45.107 に答える
6

テスト アプリを IOLoop をブロックしない形式に変換する正しい方法は次のとおりです。

from tornado.ioloop import IOLoop
import tornado.web
from tornado import gen
import time

@gen.coroutine
def async_sleep(timeout):
    """ Sleep without blocking the IOLoop. """
    yield gen.Task(IOLoop.instance().add_timeout, time.time() + timeout)

class MainHandler(tornado.web.RequestHandler):
    @gen.coroutine
    def get(self):
        print("Start request")
        yield async_sleep(4)
        print("Okay done now")
        self.write("Howdy howdy howdy")
        self.finish()

if __name__ == "__main__":
    application =  tornado.web.Application([
        (r'/', MainHandler),
    ])
    application.listen(8888)
    IOLoop.instance().start()

違いは、への呼び出しをtime.sleepIOLoop をブロックしないものに置き換えることです。Tornado は、複数のスレッド/サブプロセスを必要とせずに多数の同時 I/O を処理するように設計されていますが、同期 API を使用するとブロックされます。ロング ポーリング ソリューションで同時実行性を希望どおりに処理するには、実行時間の長い呼び出しがブロックされないようにする必要があります。

于 2014-05-26T19:13:45.557 に答える