Ternado,同步与异步

总所周知,Ternado是一个非阻塞的Web服务器,很多人因为它的高效性,因为它的短小精悍而选择使用它。同样的理由,我们也选择了使用它。但是在用了它快一年后的今天,我才发现我根本就没有使用到它真正的异步特性。

首先,什么是阻塞?什么是非阻塞?以下两张图就应该能够解释一切了:

阻塞(同步)模型

非阻塞(异步)模型

两个模型从图形上理解起来都很容易。一个是顺序执行,另外一个则是打乱顺序执行。

理论上讲,非阻塞的模型并不一定绝对拥有最佳的效率,但是它在外部调用频繁、涉及到多处IO请求的状况下,它理论上拥有更高的执行效率,因为它在同步处于等待状态下的时间段内它也依然在尽力的压榨着CPU的运算能力。而异步模型最大的问题就是频繁的在多个进程(线程)中调度过程中的CPU消耗。

那么按照Ternado中给出的示例代码的执行结果是什么?

你会惊讶的发现,它官网给出的所有代码执行出来的结果都是同步的。也就是说你根本无法真正的启用它的异步特性。


import time
import tornado.ioloop
import tornado.web


class MainHandler(tornado.web.RequestHandler):
    def get(self):
        self.write("Hello, world")


class BlockHandler(tornado.web.RequestHandler):
    def get(self):
        time.sleep(10)
        self.write("Final Over")

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

if __name__ == "__main__":
    application.listen(8888)
    tornado.ioloop.IOLoop.instance().start()

做一个简单的试验,运行这个程序,然后访问 http://127.0.0.1:8888/test/ ,页面会卡住10秒,然后在这卡住的10秒内,你访问一下http://127.0.0.1:8888/ 如果它真的是一个非阻塞的系统,它应该快速的告诉你Hello, world……但是事实却并不是如此,系统一直到那10秒的程序运行完成,才会给你返回你想要的Hello,World。

原因并不是在于Ternado本身,而是在于客户端程序员的我们。因为所有的异步架构中,所有的代码必须要全部都是异步的,而get中的代码是同步的,所以必然没办法实现ternado的真正异步非阻塞。

所以,还是像erlang一样善用进程吧,毫不吝啬的使用它们吧。

这才是真正的异步代码,这个时候再试试看同时访问 http://127.0.0.1:8888/test/ 和http://127.0.0.1:8888/ 吧


import threading
import time
import tornado.ioloop
import tornado.web
from tornado.web import asynchronous


class MainHandler(tornado.web.RequestHandler):
    def get(self):
        self.write("Hello, world")


class BlockHandler(tornado.web.RequestHandler):
    @asynchronous
    def get(self):
        Factory(self.after_done).start()

    def after_done(self, value):
        self.finish(value)


class Factory(threading.Thread):
    def __init__(self, callback=None):
        super(Factory, self).__init__()
        self.callback = callback

    def run(self):
        time.sleep(10)
        self.callback('Final Over')


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

if __name__ == "__main__":
    application.listen(8888)
    tornado.ioloop.IOLoop.instance().start()

评论

此博客中的热门博文

转一下关于Fuck的用法

远程记录OpenWRT日志

用OpenWRT打造自动翻墙路由器(详解篇)