【问题标题】:Why tornado doesn't go concurrently?为什么龙卷风不会同时发生?
【发布时间】:2015-02-07 21:15:30
【问题描述】:

以下是我的测试代码。我使用的是 Python2.7,安装了futures

pip install futures

以下是我的演示代码:

import tornado.ioloop
import tornado.web
from tornado.gen import coroutine, Task
from tornado.concurrent import Future
import time


class MainHandler(tornado.web.RequestHandler):
    @coroutine
    def get(self):
        print "in"
        res = yield Task(self._work)
        self.write(res)

    def _work(self, callback):
        time.sleep(10)
        callback("hello world!")

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

这段代码应该同时运行,不是吗?然而,事实并非如此。

我用 Firefox 和 IE 进行了测试。我想我犯了一些错误。你能指出我的错误会很好。

一次只有一个请求(http://localhost:8888/:

import tornado.ioloop
import tornado.web
from tornado.gen import coroutine, Return, Task
from tornado.process import Subprocess
from tornado.concurrent import Future
from threading import Thread
import time

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

class MainHandler(tornado.web.RequestHandler):
    @coroutine
    def get(self):
        print "in"
        res = yield self._work()
        self.write(res)

    @coroutine
    def _work(self):
        yield async_sleep(5)
        raise Return("hello world!")

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

【问题讨论】:

  • 你不能使用time.sleep,因为那会阻塞tornado的I/O循环。您需要使用非阻塞方法来代替睡眠。有关示例,请参阅受骗问题。
  • @dano sleep 这是我认为的真正问题。在实践中,我将调用一个 bash 命令,这可能需要一段时间。有什么好的建议吗?

标签: python asynchronous tornado


【解决方案1】:

由于您在 cmets 中指出您希望通过 tornado 运行子进程,因此这里有一个示例说明如何执行此操作。我还修复了一个逻辑错误,即您在调用 _work 时创建了一个 Task,这不会按照您的预期方式工作:

import tornado.ioloop
import tornado.web
from tornado.gen import coroutine, Return
from tornado.process import Subprocess
from tornado.concurrent import Future

class MainHandler(tornado.web.RequestHandler):
    @coroutine
    def get(self):
        print "in"
        res = yield self._work()
        self.write(res)

    @coroutine
    def _work(self):
        p = Subprocess(['sleep', '10'])
        f = Future()
        p.set_exit_callback(f.set_result)
        yield f
        raise Return("hello world!")

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

如你所见,我将_work 做了一个协程,然后使用tornado's built-in Subprocess 类来执行一个命令。我创建了一个Future 对象,并指示Subprocess 在完成时调用Future.set_result(return_code_of_subprocess)。然后我在Future 实例上调用了yield。这使得代码等待子进程完成,而不会阻塞 I/O 循环。

【讨论】:

  • 看起来不错。但我注意到了一个问题。我开始了两个请求,第二个请求在第一个请求返回之前无法处理。你能告诉我一种让处理程序畅通的方法吗?为方便起见,我参考了 dup 并使用async_sleep 进行模拟。请查看更新。
  • @HuStmpHrrr 你是从网络浏览器测试这个吗?如果是这样,您可能遇到了我描述的问题here。基本上,您会看到浏览器限制。如果您要同时使用两个浏览器或通过 curl 进行测试,它会正常工作。
  • 我想知道如果命令不是['sleep', '10']而是['ls', '-l'],我怎样才能将ls -l的结果返回给get(self)
【解决方案2】:

您代码中的time.sleep 是一种阻塞方法。
你需要tornado.gen.sleep(一种非阻塞方法,tornado 4.1 中的新方法)来代替。

【讨论】:

    猜你喜欢
    • 2016-02-11
    • 2014-10-05
    • 2011-10-27
    • 1970-01-01
    • 1970-01-01
    • 2015-02-26
    • 1970-01-01
    • 2017-11-29
    相关资源
    最近更新 更多