外观
异步与协程
Tornado是一款可以定制的非阻塞式的异步加载框架,为了充分利用Tornado的特性,需要先理解几个概念。
基本概念
- 阻塞
程序未得到所需资源而处于挂起的状态成为阻塞。
程序在等待某个操作完成期间,自身无法干别的事情,则称该程序在该操作上是阻塞的。最典型的例子就是Python使用input函数时,在未输入内容时下方的代码都无法执行,这就是阻塞。
- 非阻塞
程序在等待某操作过程中可以继续干别的事情,则称该程序在该操作上是非阻塞的。阻塞是无处不在的,而非阻塞并不是无处不在的。它只有程序封装的级别可以囊括独立的子程序单元时它才可能存在非阻塞状态。非阻塞的存在是因为某个操作因为是阻塞状态时会很浪费时间,效率低下,才将它变为非阻塞的。
- 同步
不同程序单元为了完成某个任务,在执行过程中需要靠某种通信方式来协调一致。如购物商场中更新商品库存,就需要用“行锁”作为通信信号,让不同的更新请求强制排队顺序执行。
- 异步
不同任务在完成某一任务时不需要反复通过通信确认一致就称为异步。不相关的程序之间为异步的。如爬虫爬取网页,调度程序调用下载程序时,即可调度其他任务,无需与该任务保持一致;不同网页的下载、保存等操作都是无关的。
- 并发
并发描述的是程序的组织结构。程序要被设计成多个可独立执行的子程序,以最大化利用有限的CPU资源,是多个任务几乎同时执行。
- 并行
并行描述的是程序的执行状态。指多个任务同时执行,以利用有限的资源加速完成某个任务。并发是一种程序执行方式,使用它可以大大加快程序的执行效率,但它不是必须的。
asyncio模块
asyncio模块是Python用来编写并发代码的内置库,使用async/wait语法。Python有很多高性能异步库都是基于asyncio开发的,包括网络和网站服务,数据库连接库、分布式任务队列。asyncio往往是构建IO密集型和高层级结构化网络代码的最佳选择。
asyncio提供了一组高层级API,用于以下任务执行。
- 并发地执行Python协程,并对其执行过程实现完全控制。
- 执行网络IO和IPC。
- 控制子进程。
- 通过队列实现分布式任务。
- 同步并发代码。
此外,还有一些低级API用以支持库和框架的开发者实现。
- 创建和管理事件循环,以提供异步API,用于网络化、运行子进程、处理OS信号等。
- 使用transports实现高效率协议。
- 通过async/await语法桥接基于回调的库和代码。
下面通过一个例子介绍asyncio的基本使用。
import asyncio
async def worker_1():
print('函数worker_1开始运行')
await asyncio.sleep(1)
print('函数worker_1执行完毕')
async def worker_2():
print('函数worker_2开始执行')
await asyncio.sleep(2)
print('函数worker_2执行完毕')
async def main():
task1 = asyncio.create_task(worker_1())
task2 = asyncio.create_task(worker_2())
print('await之前')
await task1
print('已经await task1')
await task2
print('已经await task2')
if __name__ == '__main__':
asyncio.run(main())上述代码的执行步骤如下:
- if main部分开始执行。asyncio.run()先执行,程序进入main()函数,开启事件循环。
- task1和task2被创建,并进入事件循环,等待运行,输出“await之前”。
- await task1被执行,用户选择从当前的主任务中切出,时间调度器开始调度worker_1。
- worker_1开始执行,运行到await asyncio.sleep(1)时,从当前任务切出,时间调度器开始调度worker_2。
- worker_2开始执行,运行到await asyncio.sleep(2)时,从当前任务切出,事件调度器开始在这个时候暂停调度。
- 1s后,worker_1的sleep完成,事件调度器将控制权重新传给task_1,输出“函数worker_1执行完毕”。task1完成任务,从事件调度器中退出。
- await task1完成后,事件调度器将控制权重新传给主任务,输出“已经await task1”,然后在await task2处继续等待;2s后,worker_2的sleep完成,事件调度器将控制权重新传给task2,输出“函数worker_2执行完毕”,task2完成任务,从事件循环中退出。
- 主任务输出“已经await task2”,协程全任务结束,事件循环结束。
输出结果如下。

Tornado框架的gen模块
Tornado框架使用Tornado.gen实现基于生成器的协程程序。协程程序提供了一种在异步环境中工作比链接回调更简单的方法。使用协程的代码在技术上是异步的,但它是作为单个生成器而不是单独的函数集合编写的。
例如,一个基于协程的处理程序代码如下。
from tornado import gen
class GenAsyncHandler(RequestHandler):
@gen.coroutine
def get(self):
http_client = AsyncHTTPClient()
response = yield http_client.fetch('https://example.com')
do_something_with_response(response)
self.render('template.html')上述代码中,@gen.coroutine是异步生成器的装饰器,带有此修饰符的函数将返回Future对象。
还可以生成其他可扩展对象的列表和字典,这些对象将同时启动并并行运行,结果列表或字典将在完成后返回。如:
@gen.coroutine
def get(self):
http_client = AsyncHTTPClient()
response1, response2 = yield [http_client.fetch(url1),
http_client.fetch(url2)]
response_dict = yield dict(response3=http_client.fetch(url3),
response4=http_client.fetch(url4))
response3 = response_dict['response3']
response4 = response_dict['response4']此外,如果引发异常,可以使用gen.Return()将其值参数作为协程的结果返回,如下:
@gen.coroutine
def fetch_json(url):
response = yield AsyncHTTPClient().fetch(url)
raise gen.Return(json_decode(response.body))