1. 同步

我们用两个函数来模拟两个客户端请求,并依次进行处理:

# coding:utf-8

def req_a():
"""模拟请求a"""
print '开始处理请求req_a'
print '完成处理请求req_a' def req_b():
"""模拟请求b"""
print '开始处理请求req_b'
print '完成处理请求req_b' def main():
"""模拟tornado框架,处理两个请求"""
req_a()
req_b() if __name__ == "__main__":
main()

执行结果:

开始处理请求req_a
完成处理请求req_a
开始处理请求req_b
完成处理请求req_b

同步是按部就班的依次执行,始终按照同一个步调执行,上一个步骤未执行完不会执行下一步。

想一想,如果在处理请求req_a时需要执行一个耗时的工作(如IO),其执行过程如何?

# coding:utf-8

import time

def long_io():
"""模拟耗时IO操作"""
print "开始执行IO操作"
time.sleep(5)
print "完成IO操作"
return "io result" def req_a():
print "开始处理请求req_a"
ret = long_io()
print "ret: %s" % ret
print "完成处理请求req_a" def req_b():
print "开始处理请求req_b"
print "完成处理请求req_b" def main():
req_a()
req_b() if __name__=="__main__":
main()

执行过程:

开始处理请求req_a
开始执行IO操作
完成IO操作
完成处理请求req_a
开始处理请求req_b
完成处理请求req_b

在上面的测试中,我们看到耗时的操作会将代码执行阻塞住,即req_a未处理完req_b是无法执行的。

我们怎么解决耗时操作阻塞代码执行?

2. 异步

对于耗时的过程,我们将其交给别人(如其另外一个线程)去执行,而我们继续往下处理,当别人执行完耗时操作后再将结果反馈给我们,这就是我们所说的异步。

我们用容易理解的线程机制来实现异步。

2.1 回调写法实现原理

# coding:utf-8

import time
import thread def long_io(callback):
"""将耗时的操作交给另一线程来处理"""
def fun(cb): # 回调函数作为参数
"""耗时操作"""
print "开始执行IO操作"
time.sleep(5)
print "完成IO操作,并执行回调函数"
cb("io result") # 执行回调函数
thread.start_new_thread(fun, (callback,)) # 开启线程执行耗时操作 def on_finish(ret):
"""回调函数"""
print "开始执行回调函数on_finish"
print "ret: %s" % ret
print "完成执行回调函数on_finish" def req_a():
print "开始处理请求req_a"
long_io(on_finish)
print "离开处理请求req_a" def req_b():
print "开始处理请求req_b"
time.sleep(2) # 添加此句来突出显示程序执行的过程
print "完成处理请求req_b" def main():
req_a()
req_b()
while 1: # 添加此句防止程序退出,保证线程可以执行完
pass if __name__ == '__main__':
main()

执行过程:

开始处理请求req_a
离开处理请求req_a
开始处理请求req_b
开始执行IO操作
完成处理请求req_b
完成IO操作,并执行回调函数
开始执行回调函数on_finish
ret: io result
完成执行回调函数on_finish

异步的特点是程序存在多个步调,即本属于同一个过程的代码可能在不同的步调上同时执行。

2.2 协程写法实现原理

在使用回调函数写异步程序时,需将本属于一个执行逻辑(处理请求a)的代码拆分成两个函数req_a和on_finish,这与同步程序的写法相差很大。而同步程序更便于理解业务逻辑,所以我们能否用同步代码的写法来编写异步程序?

回想yield关键字的作用?

初始版本

# coding:utf-8

import time
import thread gen = None # 全局生成器,供long_io使用 def long_io():
def fun():
print "开始执行IO操作"
global gen
time.sleep(5)
try:
print "完成IO操作,并send结果唤醒挂起程序继续执行"
gen.send("io result") # 使用send返回结果并唤醒程序继续执行
except StopIteration: # 捕获生成器完成迭代,防止程序退出
pass
thread.start_new_thread(fun, ()) def req_a():
print "开始处理请求req_a"
ret = yield long_io()
print "ret: %s" % ret
print "完成处理请求req_a" def req_b():
print "开始处理请求req_b"
time.sleep(2)
print "完成处理请求req_b" def main():
global gen
gen = req_a()
gen.next() # 开启生成器req_a的执行
req_b()
while 1:
pass if __name__ == '__main__':
main()

执行过程:

开始处理请求req_a
开始处理请求req_b
开始执行IO操作
完成处理请求req_b
完成IO操作,并send结果唤醒挂起程序继续执行
ret: io result
完成处理请求req_a

升级版本

我们在上面编写出的版本虽然req_a的编写方式很类似与同步代码,但是在main中调用req_a的时候却不能将其简单的视为普通函数,而是需要作为生成器对待。

现在,我们试图尝试修改,让req_a与main的编写都类似与同步代码。

# coding:utf-8

import time
import thread gen = None # 全局生成器,供long_io使用 def gen_coroutine(f):
def wrapper(*args, **kwargs):
global gen
gen = f()
gen.next()
return wrapper def long_io():
def fun():
print "开始执行IO操作"
global gen
time.sleep(5)
try:
print "完成IO操作,并send结果唤醒挂起程序继续执行"
gen.send("io result") # 使用send返回结果并唤醒程序继续执行
except StopIteration: # 捕获生成器完成迭代,防止程序退出
pass
thread.start_new_thread(fun, ()) @gen_coroutine
def req_a():
print "开始处理请求req_a"
ret = yield long_io()
print "ret: %s" % ret
print "完成处理请求req_a" def req_b():
print "开始处理请求req_b"
time.sleep(2)
print "完成处理请求req_b" def main():
req_a()
req_b()
while 1:
pass if __name__ == '__main__':
main()

执行过程:

开始处理请求req_a
开始处理请求req_b
开始执行IO操作
完成处理请求req_b
完成IO操作,并send结果唤醒挂起程序继续执行
ret: io result
完成处理请求req_a

最终版本

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

# coding:utf-8

import time
import thread def gen_coroutine(f):
def wrapper(*args, **kwargs):
gen_f = f() # gen_f为生成器req_a
r = gen_f.next() # r为生成器long_io
def fun(g):
ret = g.next() # 执行生成器long_io
try:
gen_f.send(ret) # 将结果返回给req_a并使其继续执行
except StopIteration:
pass
thread.start_new_thread(fun, (r,))
return wrapper def long_io():
print "开始执行IO操作"
time.sleep(5)
print "完成IO操作,yield回操作结果"
yield "io result" @gen_coroutine
def req_a():
print "开始处理请求req_a"
ret = yield long_io()
print "ret: %s" % ret
print "完成处理请求req_a" def req_b():
print "开始处理请求req_b"
time.sleep(2)
print "完成处理请求req_b" def main():
req_a()
req_b()
while 1:
pass if __name__ == '__main__':
main()

执行过程:

开始处理请求req_a
开始处理请求req_b
开始执行IO操作
完成处理请求req_b
完成IO操作,yield回操作结果
ret: io result
完成处理请求req_a

这个最终版本就是理解Tornado异步编程原理的最简易模型,但是,Tornado实现异步的机制不是线程,而是epoll,即将异步过程交给epoll执行并进行监视回调。

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

思考

  1. Tornado里的异步就是协程,这句话对吗?
  2. Tornado中出现yield就是异步,这句话对吗?
  3. 怎么理解yield将程序挂起?在Tornado中又如何理解yield挂起程序实现异步?

tornado异步(1)的更多相关文章

  1. tornado异步请求的理解(转)

    tornado异步请求的理解 http://www.kankanews.com/ICkengine/archives/88953.shtml 官网第一段话: Tornado is a Python w ...

  2. 使用Tornado异步接入第三方(支付宝)支付

    目前国内比较流行的第三方支付主要有支付宝和微信支付,博主最近研究了下如何用Python接入支付宝支付,这里我以Tornado作为web框架,接入支付宝构造支付接口. 使用Tornado异步接入支付宝支 ...

  3. Tornado异步非阻塞的使用以及原理

    Tornado 和现在的主流 Web 服务器框架(包括大多数 Python 的框架)有着明显的区别:它是非阻塞式服务器,而且速度相当快.得利于其 非阻塞的方式和对 epoll 的运用,Tornado ...

  4. Python Web框架 tornado 异步原理

    Python Web框架 tornado 异步原理 参考:http://www.jb51.net/article/64747.htm 待整理

  5. Tornado异步(2)

    Tornado异步 因为epoll主要是用来解决网络IO的并发问题,所以Tornado的异步编程也主要体现在网络IO的异步上,即异步Web请求. 1. tornado.httpclient.Async ...

  6. tornado异步请求响应速度的实例测试

    tornado异步请求响应速度的实例测试

  7. 5.(基础)tornado异步

    终于到了传说中的异步了,感觉异步这个名字听起来就很酷酷的,以前还不是多擅长Python时,就跑去看twisted的源码,结果给我幼小的心灵留下了创伤.反正包括我在内,都知道异步编程很强大,但是却很少在 ...

  8. Python web框架 Tornado异步非阻塞

    Python web框架 Tornado异步非阻塞   异步非阻塞 阻塞式:(适用于所有框架,Django,Flask,Tornado,Bottle) 一个请求到来未处理完成,后续一直等待 解决方案: ...

  9. 7.2 Tornado异步

    7.2 Tornado异步 因为epoll主要是用来解决网络IO的并发问题,所以Tornado的异步编程也主要体现在网络IO的异步上,即异步Web请求. 1. tornado.httpclient.A ...

随机推荐

  1. WARNING:tensorflow:From /usr/lib/python2.7/site-packages/tensorflow/python/util/tf_should_use.py:189: initialize_all_variables (from tensorflow.python.ops.variables) is deprecated and will be removed

    initialize_all_variables已被弃用,将在2017-03-02之后删除. 说明更新:使用tf.global_variables_initializer代替. 就把tf.initia ...

  2. #include <sys/epoll.h> epoll - I/O event notification facility 服务器端 epoll(7) - Linux manual page http://www.man7.org/linux/man-pages/man7/epoll.7.html

    epoll使用详解(精髓) - Boblim - 博客园 https://www.cnblogs.com/fnlingnzb-learner/p/5835573.html epoll使用详解(精髓) ...

  3. Using the FutureRequestExecutionService Based on classic (blocking) I/O handle a great number of concurrent connections is more important than performance in terms of a raw data throughput

    Chapter 7. Advanced topics http://hc.apache.org/httpcomponents-client-ga/tutorial/html/advanced.html ...

  4. Quick UDP Internet Connections 让互联网更快的协议,QUIC在腾讯的实践及性能优化

    https://mp.weixin.qq.com/s/44ysXnVBUq_nJByMyX9n5A 让互联网更快:通往QUIC之路 原创: 史天 翻译 云技术实践 8月15日 QUIC(Quick U ...

  5. Proud Merchants---hdu3466(有01背包)

    题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=3466 与顺序有关的01背包. 如果一个物品p = 5,q = 7,一个物品p = 5,q = 9,如果 ...

  6. java7(3)——增强的catch之自动释放资源

    跟mutilcatch一样,java7提供了自动释放资源的方法,但还是很少看到人使用,估计是麻烦去重写close方法.不过jdk内部一些类已经改成使用增强的catch来释放资源的写法,所以我们有必要了 ...

  7. zabbix 添加 微信、邮件 媒介详解

    1:zabbix 添加 微信.邮件 媒介.  1.2:发送告警邮件         1:一次完整的监控流程可以简单描述为:         Host Groups (设备组) ->Hosts ( ...

  8. IT开发工程师的悲哀现状和可能前途

    IT开发工程师的悲哀现状和可能前途 本文所指的开发工程师,仅指程序开发人员和以数字电路开发为主的电子工程师.当你选择计算机或者电子.自控等专业进入大学时,你本来还是有机会从事其它行业的,可你毕业时执迷 ...

  9. node.js---sails项目开发

    http://sailsdoc.swift.ren/ 这里有 sails中文文档 node.js---sails项目开发(1)安装,启动sails node.js---sails项目开发(2)安装测试 ...

  10. 教你编译PHP7 (nginx+mysql+php7)

    # 安装编译工具: yum install gcc automake autoconf libtool gcc-c++ # 安装基础库 yum install gd zlib zlib-devel o ...