tornado十二:同步与异步

一、同步、异步

同步:按部就班的依次执行

异步:对于耗时的操作,交给别人(另一个线程)处理;我们继续向下去执行另一个请求;当别人结束耗时操作后反馈给我们

同步示例,模仿客户端A、B请求服务器main():

# _*_ coding: utf-8 _*_
import time
# 模拟handler获取数据(数据库、其它服务器、循环耗时)
def longIo():
    print(u"开始耗时操作")
    time.sleep(5)
    print(u"结束耗时操作")
    return u"longIo(handler)返回的数据"
# 客户端A请求
def reqA():
    print(u"开始处理reqA")
    res = longIo()
    print(u"接收到longIo(handler)的响应数据:%s" % res)
    print(u"开始结束reqA")
def reqB():
    print(u"开始处理reqB")
    print(u"开始结束reqB")
# 模拟tornado服务器
def main():
    reqA()
    reqB()
    # while True:
    #     time.sleep(0.01)
    #     pass
if __name__ == '__main__':
    main()

 

 

处理流程:处理完A请求,再处理B请求.....B请求会一直等待A请求处理完,再执行B请求

开始处理reqA
开始耗时操作
结束耗时操作
接收到longIo(handler)的响应数据:longIo(handler)返回的数据
开始结束reqA
开始处理reqB
开始结束reqB

 

 异步:对于耗时的操作,交给另一个线程去处理,我们继续往下执行;当此耗时操作结束的时侯,将结果反馈给我们。

实现异步的两种方式:回调函数和协程实现异步

 

二、用回调函数的方式实现异步:类似于ajax

将耗时操作,另起一个线程去处理,示例:

# _*_ coding: utf-8 _*_
import time
import threading
def longIo(timeout):
    # 将耗时操作交给另一个线程处理
    def run():
        print(u"开始耗时操作")
        time.sleep(timeout)
        print(u"结束耗时操作")
        return u"longIo(handler)返回的数据"
    threading.Thread(target=run).start()
def reqA():
    print(u"开始处理reqA")
    res = longIo(5)
    print(u"接收到longIo(handler)的响应数据:%s" % res)
    print(u"开始结束reqA")
def reqB():
    print(u"开始处理reqB")
    time.sleep(2)
    print(u"开始结束reqB")
def main():
    reqA()
    reqB()
if __name__ == '__main__':
    main()

 

 

 

处理流程:

处理A请求,并给耗时操作(handler)另起一个线程去处理,耗时操作被挂起,挂起之后的代码继续执行,结束A请求,然后处理B请求

被另一个线程挂起的longIo,并行与主线程执行!

当A请求的耗时操作结束时,返回A请求的耗时操作;当B请求的操作结束时,返回B请求;耗时操作的线程,和处理请求AB的主线程,两者互不等待!!!

开始处理reqA
开始耗时操作
接收到longIo(handler)的响应数据:None
开始结束reqA
开始处理reqB
开始结束reqB
# ....A请求的耗时操作..... 结束耗时操作

 

上面按照上面的方法,实现了并行,实现了异步。

但是,有个问题,接收不到longIo(handler)耗时操作run方法返回的数据。

原因:不能使用return返回数据!!!

使用回调函数时,也不能使用res接收返回的数据;使用协程异步时,要使用res接收返回的对象,但同样不能使用return返回数据。

 

异步的时侯,不能使用return直接返回耗时操作longIo(handler)的返回数据!那要怎么才能接收到耗时操作longIo(handler)返回的数据呢?

解决办法:

使用回调函数或协程异步,异步的返回数据!!!

因此,需要定义一个回调函数:finish!在耗时操作结束时,执行回调函数,耗时操作的数据当参数传给回调函数,回调函数返回耗时操作的数据!

# _*_ coding: utf-8 _*_
import time
import threading
def longIo(callback):
    # 将耗时操作交给另一个线程处理
    def run(cb):
        print(u"开始耗时操作")
        time.sleep(5)
        print(u"结束耗时操作")
        # 回调函数和协程异步:都不能使用return返回数据
        # return u"longIo(handler)返回的数据"
        return_ata = u"longIo(handler)返回的数据"
        # 耗时线程结束时,调用回调函数返回耗时操作的数据
        cb(return_ata)
    threading.Thread(target=run, args=(callback,)).start()
def finish(data):
    print(u"开始处理回调函数")
    print(u"接收到longIo(handler)的响应数据:%s" % data)
    print(u"结束回调函数")
    # 回调函数中,仍然不能使用return返回数据
    # return u"接收到longIo(handler)的响应数据:%s" % data

def reqA():
    print(u"开始处理reqA")
    # 使用回调函数时,不能使用res接收返回的数据
    longIo(finish)
    print(u"开始结束reqA")
def reqB():
    print(u"开始处理reqB")
    time.sleep(2)
    print(u"开始结束reqB")
def main():
    reqA()
    reqB()
if __name__ == '__main__':
    main()

 

 

处理流程:

处理A请求,并给耗时操作(handler)另起一个线程去处理,结束A请求;开始处理B请求

当A请求的耗时操作结束时,执行回调函数finish,返回耗时操作的数据;当B请求的操作结束时,返回B请求;两者互不等待!!!

开始处理reqA
开始耗时操作
开始结束reqA
开始处理reqB
开始结束reqB
# 耗时操作进程中......
结束耗时操作
开始处理回调函数
# 使用回调函数返回耗时操作的数据,而不是return
接收到longIo(handler)的响应数据:longIo(handler)返回的数据
结束回调函数

 

 

 

 

三、用协程实现异步

使用协程实现异步的四个步骤:

1.在请求A中,使用yield longIo(),实现协程处理longIo(handler)

2.在服务器中,得到请求A的生成器:gen = reqA();并使用next(gen)执行生成器,即执行longIo(handler)协程。

3.将此生成器,定义为全局变量,使它可以在longIo方法中使用。

4.在耗时操作longIo(handler)中,使用上面的生成器对象的send方法,将longIo的数据返回

 

# _*_ coding: utf-8 _*_
import time
import threading
gen = None
def longIo():
    def run():
        print(u"开始耗时操作")
        time.sleep(10)
        print(u"结束耗时操作")
        try:
            # 4.1在耗时操作longIo(handler)中,得到请求A的生成器:gen = reqA()
            global gen
            # 4.2使用生成器的send方法,将数据返回
            gen.send(u"longIo(handler)返回的数据")
        except StopIteration as e:
            pass
    threading.Thread(target=run).start()

def reqA():
    print(u"开始处理reqA")
    # 1.yield func():用协程执行longIo
    # 在执行next之前,生成器将挂起
    #       在首次挂起时:yield之前的代码会执行;
    # 在执行next之后,yield之后的代码将执行,直到下一个yield
    #       如果只有一个yield,那么会从yield之后的代码,到yield之前的代码被执行
    # 在挂起时,就交由协程去处理,那么就结束A请求了,服务器可以处理B请求了
    res = yield longIo()
    print(u"接收到longIo(handler)的响应数据:%s" % res)
    print(u"开始结束reqA")
def reqB():
    print(u"开始处理reqB")
    time.sleep(2)
    print(u"开始结束reqB")
def main():
    # 3.2 将此生成器定义为全局变量
    global gen
    # 2.1 协程的结果:生成器
    # 得到协程reqA()的生成器:gen
    gen = reqA()
    # 2.2取出生成器的数据
    next(gen)
    # 3.1 在longIo中,使用生成器的send方法,返回数据。因此先要将gen定义为全局变量,这里的gen要在longIo可调用
    reqB()
    while True:
        time.sleep(0.1)
if __name__ == '__main__':
    main()

 

处理流程:得到A请求(协程)将它作为生成器,开始处理A请求,next(A请求生成器),开始执行yield long();此时,A请求交给线程处理longIo,同时被挂起;

A被挂起以后,A交出服务器的使用权,服务器可以处理其它请求了,但不会结束A请求,这与回调函数不一样;此时,可以开始处理B请求;此时,两者互不等待。

当A请求的耗时操作处理完成后,调用由A请求得到的生成器的send方法,将耗时操作的数据返回;这个过程中,已经交出服务器的使用权,不会影响其它客户端继续请求;因此实现了-----同步请求,异步返回的高并发。

开始处理reqA
开始耗时操作
开始处理reqB
开始结束reqB
# .....耗时操作进行中.....
结束耗时操作
接收到longIo(handler)的响应数据:longIo(handler)返回的数据
开始结束reqA

 

posted on 2018-08-11 11:07  myworldworld  阅读(251)  评论(0)    收藏  举报

导航