tornado教程-异步与WebSocket-认识异步

1、同步

两个函数来模拟两个客户端请求

1、一般模式

import time
#请求A
def reqA():
    print("开始处理reqA")
    print("结束处理reqA")

#请求B
def reqB():
    print("开始处理reqB")
    print("结束处理reqB")

#Tornado
def main():
    reqA()
    reqB()
    while 1:
        time.sleep(0.1)

if __name__ == "__main__":
    main()

执行结果:

开始处理reqA
结束处理reqA
开始处理reqB
结束处理reqB

SO

同步是按部就班的依次执行,从上到下,但是如果有耗时操作的话就会发生下面情况

2、模仿带耗时操作

import time

def longIo():
    print("开始耗时操作")
    time.sleep(5)
    print("结束耗时操作")
    return "sunck is a good man"

#请求A
def reqA():
    print("开始处理reqA")
    res = longIo()
    print("接收到:",res)
    print("结束处理reqA")

#请求B
def reqB():
    print("开始处理reqB")
    time.sleep(2)
    print("结束处理reqB")

#Tornado
def main():
    time.clock()
    reqA()
    reqB()
    print("耗时:",time.clock())
    while 1:
        time.sleep(0.1)

if __name__ == "__main__":
    main()

执行结果

开始处理reqA
开始耗时操作
结束耗时操作
接收到: sunck is a good man
结束处理reqA
开始处理reqB
结束处理reqB
耗时: 7.001181502708876

SO

当遇到耗时操作的时候,就会出现阻塞等待。

2、 异步

1、开启线程写法实现原理

import time
import threading

def longIo(func):
    def run(f):
        print("开始耗时操作")
        time.sleep(5)
        print("结束耗时操作")
        f("sunck is a good man")
    threading.Thread(target=run, args=(func,)).start()

def finish(data):
    print("开始处理回调函数")
    print("收到:", data)
    print("结束处理回调函数")
    print("耗时:", time.clock())

#请求A
def reqA():
    print("开始处理reqA")
    longIo(finish)
    print("结束处理reqA")

#请求B
def reqB():
    print("开始处理reqB")
    time.sleep(2)
    print("结束处理reqB")

#Tornado
def main():
    time.clock()
    reqA()
    reqB()

    while 1:
        time.sleep(0.1)

if __name__ == "__main__":
    main()

执行结果

开始处理reqA
开始耗时操作
结束处理reqA
开始处理reqB
结束处理reqB
结束耗时操作
开始处理回调函数
收到: sunck is a good man
结束处理回调函数
耗时: 5.001214755564824

SO

对于耗时的操作,我们会交给别人(其他的线程)去处理,而主程序继续向下执行,当别人执行完耗时操作将结果反馈给主线程,在处理接收的数据

2、回调函数实现异步

import time
import threading

gen = None

def longIo():
    def run():
        print("开始耗时操作")
        time.sleep(5)
        try:
            print("结束耗时操作")
            global gen
            gen.send("sunck is a good man")
        except StopIteration as e:
            pass
    threading.Thread(target=run).start()

#请求A
def reqA():
    print("开始处理reqA")
    res = yield longIo()
    print("接收到:", res)
    print("结束处理reqA")
    print("耗时:", time.clock())

#请求B
def reqB():
    print("开始处理reqB")
    time.sleep(2)
    print("结束处理reqB")

#Tornado
def main():
    time.clock()
    global gen
    gen = reqA()
    next(gen)
    reqB()


    while 1:
        time.sleep(0.1)

if __name__ == "__main__":
    main()

执行结果

开始处理reqA
开始耗时操作
开始处理reqB
结束处理reqB
结束耗时操作
接收到: sunck is a good man
结束处理reqA
耗时: 5.000960638060726

SO

有了进一步进展,上面方法中将请求A当成了生成器来处理,对于服务器来说每个客户端理应与其他的的请求一视同仁

3、协程实现异步

import time
import threading

gen = None

def longIo():
    def run():
        print("开始耗时操作")
        time.sleep(5)
        try:
            print("结束耗时操作")
            global gen
            gen.send("sunck is a good man")
        except StopIteration as e:
            pass
    threading.Thread(target=run).start()

#装饰器,目的在执行A之前得到A的生成器
def genCoroutine(func):
    def wrapper(*args, **kwargs):
        global gen
        gen = func(*args, **kwargs)
        next(gen)
    return wrapper
#请求A
@genCoroutine
def reqA():
    print("开始处理reqA")
    res = yield longIo()
    print("接收到:", res)
    print("结束处理reqA")
    print("耗时:", time.clock())

#请求B
def reqB():
    print("开始处理reqB")
    time.sleep(2)
    print("结束处理reqB")

#Tornado
def main():
    time.clock()
    # global gen
    # gen = reqA()
    # next(gen)
    reqA()
    reqB()


    while 1:
        time.sleep(0.1)

if __name__ == "__main__":
    main()

执行结果

开始处理reqA
开始耗时操作
开始处理reqB
结束处理reqB
结束耗时操作
接收到: sunck is a good man
结束处理reqA
耗时: 5.00152059973373

SO

刚刚完成的版本依然不理想,因为存在一个全局变量gen来供long_io使用。我们现在再次改写程序,消除全局变量gen。

3、最终版

import time
import threading

#装饰器,目的在执行A之前得到A的生成器
def genCoroutine(func):
    def wrapper(*args, **kwargs):
        gen = func()#得到reqA
        g   = next(gen)#得到longIo的生成器
        def run(s):
            res = next(s)
            try:
                #将结果返回给reqA,并唤醒reqA
                gen.send(res)
            except StopIteration as e:
                pass
        threading.Thread(target=run,args=(g,)).start()
    return wrapper

def longIo():
    print("开始耗时操作")
    time.sleep(5)
    print("结束耗时操作")
    yield "sunck is a good man"
#请求A
@genCoroutine
def reqA():
    print("开始处理reqA")
    res = yield longIo()
    print("接收到:", res)
    print("结束处理reqA")
    print("耗时:", time.clock())

#请求B
def reqB():
    print("开始处理reqB")
    time.sleep(2)
    print("结束处理reqB")

#Tornado
def main():
    time.clock()
    reqA()
    reqB()
    while 1:
        time.sleep(0.1)

if __name__ == "__main__":
    main()

执行结果

开始处理reqA
开始耗时操作
开始处理reqB
结束处理reqB
结束耗时操作
接收到: sunck is a good man
结束处理reqA
耗时: 5.001927434057739

SO

需要注意的一点是,我们实现的版本严格意义上来说不能算是协程,因为两个程序的挂起与唤醒是在两个线程上实现的,而Tornado利用epoll来实现异步,程序的挂起与唤醒始终在一个线程上,由Tornado自己来调度,属于真正意义上的协程。

更新时间:2014-10-10 19:26:39

本文由 智慧煮粥 创作,如果您觉得本文不错,请随意赞赏
采用 知识共享署名4.0 国际许可协议进行许可
本站文章除注明转载/出处外,均为本站原创或翻译,转载前请务必署名
原文链接:http://zz.zzhub.cn/archives/tornado教程-异步与websocket-认识异步
最后更新:2014-10-10 19:26:39

评论

Your browser is out of date!

Update your browser to view this website correctly. Update my browser now

×