tornado 的协程调度原理
date
Mar 14, 2021
slug
python-tornado-coroutine
status
Published
tags
Python
协程
summary
讨论 tornado 中协程是怎样执行的
type
Post
本文讨论 tornado 的协程实现原理,简单做了一份笔记。
首先看一段最常见的 tornado web 代码:
其中最后一行代码
tornado.ioloop.IOLoop.current().start()
启动服务。带着几个问题往下看:- 知道 yield 可以暂存执行状态,等「合适的时机」重新恢复执行,那么保存的状态到哪去了?
- 上一个问题中「合适的时机」是到底是什么时候?
- 继续接上一个问题,具体是怎么恢复执行的?
IOLoop 类相当于是对多路复用的封装,起到事件循环的作用,调度整个协程执行过程。
查看 IOLoop 的源码,可以看到 IOLoop 继承自 Configurable,PollIOLoop 又继承自 IOLoop。当 IOLoop 启动时,会确定使用哪一种多路复用方式,epoll、kqueue 还是 select?
PollIOLoop 中 initalize 方法中调用 add_handler 方法,注册对应事件的处理函数,如 socket 可读时,回调哪个函数去处理。
IOLoop 和协程之间的信使:Future
Future 对象起到“占位符”的作用,协程的执行结果会通过 set_result 方式写入其中,并调用通过 add_done_callback 设置的回调。
恢复唤醒协程的 Runner
协程每生成一个 Future,都会生成对应的一个 Runner,并将 Future 初始化注入都其中。Runner 的
run
方法中,通过 self.gen.send(Future)
来启动 Future,当 Future 完成时,将其设置成 done,并回调其预设的 callback。回答第一个问题:协程的状态保存到哪去了:
IOLoop 中通过 add_future 调用实现类 PollIOLoop 中的 add_callback 方法,其中通过 functools 生成偏函数,放入 _callbacks 列表,等待被回调执行。
第二个问题:「合适的时机」是什么?
IOLoop 实际上就是对多路复用的封装,当底层 epoll_wait 事件发生时,即会通知 IOLoop 主线程。
这一段是 IOLoop 中等待多路复用的事件,以及处理事件。
第三个问题:具体是怎么恢复的。
Runner 通过不断 check Future 的状态,最后调用 callback 来返回结果。
总结
首先 tornado 对多路复用系统调用做了封装,来实现非阻塞 web 服务。
其次 tornado 通过 yield+Future+Runner 实现了生成 Future,Runner 监控结果,回调 callback 来实现协程的执行。