在学习tornado 如何编写异步代码之前,首先需要了解什么是异步和阻塞,这两个概念联系十分紧密且常常交换使用,但他们并不完全相同。
在讨论阻塞时,必须先设定场景,阻塞这个概念,必须放在单个线程内讨论才能真正理解其含义。
当你调用一个函数时,暂且不关心函数内做了什么,在函数返回结果之前,整个程序阻塞在函数调用的这行代码上,任何其他语句都不能被执行。
def do_something():
pass
do_something()
# 只有等到do_something执行结束,后面的代码才能被执行。
函数的执行需要时间,有时很长,有时很短,被阻塞的原因也会不一样,可能是磁盘IO,网络IO,也可能占用CPU做很重的计算。
我们所编写的web程序,不论你采用多进程部署还是多线程部署,从单个线程的视角来看,一旦遇到一个阻塞时间很长的函数,那么这个线程都不能再处理其他请求。
异步函数在执行结束前就返回了,通常在程序里触发一些动作并在后台做一些任务。这里要注意一点,所谓结束前就返回了,并不是返回了最终结果,异步函数触发了获取最终结果的动作,等到最终结果出现时再去处理最终结果。
下面是几种类型的异步接口:
下面用最简单的例子来做说明
from tornado.httpclient import AsyncHTTPClient
def asynchronous_fetch(url, callback):
http_client = AsyncHTTPClient()
def handle_response(response):
callback(response.body)
http_client.fetch(url, callback=handle_response)
asynchronous_fetch 函数向url发起网络请求,假设这个请求需要1秒钟的时间,如果是同步函数,那么这个函数执行时长最少为1秒,但使用异步函数,则是瞬间完成执行。
但asynchronous_fetch函数内触发了请求url 的这个动作,只是在asynchronous_fetch内并不等待结果的返回,这就是http_client.fetch(url, callback=handle_response) 所做的事情。1秒钟以后,程序一定会得到请求url的结果,但此时asynchronous_fetch早在1秒钟之前就结束了,谁来处理这个最终结果呢?答案是callback。
在调用asynchronous_fetch函数时,除了url参数外,还要指定callback参数,必须为它传入一个函数,专门用来处理response.body,现在,我们来梳理一下整个异步过程:
这里你必须明确并坚信一点,在第2步里,fetch方法在进行网络IO时,不会阻塞程序,因此asynchronous_fetch也不会阻塞程序,于是乎在调用完asynchronous_fetch函数后,你可以立即做其他事情,1秒钟以后再去处理请求的结果。
这便是关于异步的理解,想要更深入的理解异步,你必须深入到socket层面去学习理解select 和 epoll 模型,从根本上讲,IO多路复用是网络异步编程的基础,这里不做展开。
QQ交流群: 211426309