问题与解答¶

为什么在例子中调用 time.sleep() 不会并发执行?¶

Many people’s first foray into Tornado’s concurrency looks something likethis:

  1. class BadExampleHandler(RequestHandler):
  2. def get(self):
  3. for i in range(5):
  4. print(i)
  5. time.sleep(1)

Fetch this handler twice at the same time and you’ll see that the secondfive-second countdown doesn’t start until the first one has completelyfinished. The reason for this is that time.sleep is a blockingfunction: it doesn’t allow control to return to the IOLoop so that otherhandlers can be run.

Of course, time.sleep is really just a placeholder in these examples,the point is to show what happens when something in a handler gets slow.No matter what the real code is doing, to achieve concurrency blockingcode must be replaced with non-blocking equivalents. This means one of three things:

    class CoroutineSleepHandler(RequestHandler):
    @gen.coroutine
    def get(self):
    for i in range(5):
    print(i)
    yield gen.sleep(1)

    When this option is available, it is usually the best approach.See the Tornado wikifor links to asynchronous libraries that may be useful.

    • Find a callback-based equivalent. Similar to the first option,callback-based libraries are available for many tasks, although theyare slightly more complicated to use than a library designed forcoroutines. These are typically used with tornado.gen.Task as anadapter:

    class CoroutineTimeoutHandler(RequestHandler):
    @gen.coroutine
    def get(self):
    io_loop = IOLoop.current()
    for i in range(5):
    print(i)
    yield gen.Task(io_loop.add_timeout, io_loop.time() + 1)

    Again, theTornado wikican be useful to find suitable libraries.

    • Run the blocking code on another thread. When asynchronous librariesare not available, concurrent.futures.ThreadPoolExecutor can be usedto run any blocking code on another thread. This is a universal solutionthat can be used for any blocking function whether an asynchronouscounterpart exists or not:

    executor = concurrent.futures.ThreadPoolExecutor(8)

    class ThreadPoolHandler(RequestHandler):
    @gen.coroutine
    def get(self):
    for i in range(5):
    print(i)
    yield executor.submit(time.sleep, 1)


See the Asynchronous I/O chapter of the Tornadouser’s guide for more on blocking and asynchronous functions.

我的代码是异步的, 但它不能在两个浏览器标签页上并行运行.¶

即使你是用了异步和非阻塞的控制器, 你会发现在测试过程中可能也会出现问题.流量器将会发现你试图在两个不同的标签页加载同一个页面,这时浏览器会延迟加载第二个页面一直等到第一个页面加载完成. 如果你想要看到异步的效果,请尝试以下两种方法中的任意一个:

原文:

https://tornado-zh-cn.readthedocs.io/zh_CN/latest/faq.html