龙卷风如何在龙卷风中发挥作用?

时间:2013-05-07 20:44:30

标签: python asynchronous tornado

在Tornado的chat demo中,它有一个这样的方法:

@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),
])

如果我连续发出两个请求(即我打开了两个浏览器窗口并快速刷新),我会看到:

Start request
Start request
Okay done now
Okay done now

相反,我看到了

Start request
Okay done now
Start request
Okay done now

这让我相信它实际上是在这种情况下阻止。为什么我的代码是阻塞的,我如何获得一些代码来做我期望的呢?我在Windows 7上使用核心i7获得了相同的输出,在Linux Mint 13盒子中获得相同的输出,我认为是两个核心。

编辑:

我找到了一种方法 - 如果有人能提供一种跨平台工作的方法(我不太担心性能,只是它是非阻塞的),我会接受这个答案。

3 个答案:

答案 0 :(得分: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.sleep的调用替换为不会阻止IOLoop的调用。 Tornado旨在处理大量并发I / O而无需多个线程/子进程,但如果使用同步API,它仍会阻塞。为了使您的长轮询解决方案能够按照您的方式处理并发,您必须确保没有长时间运行的调用阻止。

答案 1 :(得分:5)

原始问题中代码的问题在于,当您调用time.sleep(4)时,您实际上阻止了事件循环的执行4秒。并且接受的答案也没有解决问题(恕我直言)。

Tornado中的异步服务依赖于信任。 Tornado会在发生任何事情时调用您的函数,但它相信您会尽快将控制权返回给它。如果您使用time.sleep()阻止,则此信任被破坏 - Tornado无法处理新连接。

使用多个线程只能隐藏错误;使用数千个线程运行Tornado(因此您可以同时提供1000个连接)将是非常低效的。适当的方法是运行一个只阻止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。一旦删除了身份验证,就会得到一个非常好的异步长轮询服务器示例。

答案 2 :(得分:1)

自Tornado 5.0起,asyncio自动启用,因此只需将time.sleep(4)更改为await asyncio.sleep(4)@tornado.web.asynchronous def get(self):更改为async def get(self):即可解决问题。

示例:

import tornado.ioloop
import tornado.web
import asyncio

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

app =  tornado.web.Application([
    (r'/', MainHandler),
])
app.listen(8888)
tornado.ioloop.IOLoop.current().start()

输出:

Start request
Start request
Okay done now
Okay done now

来源: