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) 收藏 举报