深入理解python3.4中Asyncio库与Node.js的异步IO机制

译者前言

  • 如何用yield以及多路复用机制实现一个基于协程的异步事件框架?
  • 现有的组件中yield from是如何工作的,值又是如何被传入yield from表达式的?
  • 在这个yield from之上,是如何在一个线程内实现一个调度机制去调度协程的?
  • 协程中调用协程的调用栈是如何管理的?
  • gevent和tornado是基于greenlet协程库实现的异步事件框架,greenlet和asyncio在协程实现的原理又有什么区别?

去年稍微深入地了解了下nodejs,啃完了 朴灵《深入浅出Node.js》,自己也稍微看了看nodejs的源码,对于它的异步事件机制还是有一个大致的轮廓的。虽然说让自己写一个类似的机制去实现异步事件比较麻烦,但也并不是完全没有思路。

 

而对于python中并发我还仅仅停留在使用框架的水平,对于里面是怎么实现的一点想法也没有,出于这部分实现原理的好奇,尝试读了一个晚上asyncio库的源码,感觉还是一头雾水。像这样一个较为成熟的库内容太多了,有好多冗余的模块挡在核心细节之前,确实会对学习有比较大的阻碍。

我也搜了很多国内的关于asyncio以及python中coroutine的文章,但都感觉还没到那个意思,不解渴~在网上找到了这篇文章并阅读之后,我顿时有种醍醐灌顶的感觉,因此决定把这篇长文翻译出来,献给国内同样想了解这部分的朋友们。这篇文章能很好的解答我最前的4个问题,对于第5个问题,还有待去研究greenlet的实现原理。

前言

我花了一个夏天的时间在Node.js的web框架上,那是我第一次全职用Node.js工作。在使用了几周后,有一件事变得很清晰,那就是我们组的工程师包括我都对Node.js中异步事件机制缺乏了解,也不清楚它底层是怎么实现的。我深信,对一个框架使用非常熟练高效,一定是基于对它的实现原理了解非常深刻之上的。所以我决定去深挖它。这份好奇和执着最后不仅停留在Node.js上,同时也延伸到了对其它语言中异步事件机制的实现,尤其是python。我也是拿python来开刀,去学习和实践的。于是我接触到了python 3.4的异步IO库 asyncio,它同时也和我对协程(coroutine)的兴趣不谋而合,可以参考我的那篇关于生成器和协程的博客(译者注:因为asyncio的异步IO是用协程实现的)。这篇博客是为了回答我在研究那篇博客时产生的问题,同时也希望能解答朋友们的一些疑惑。

这篇博客中所有的代码都是基于Python 3.4的。这是因为Python 3.4同时引入了 selectorsasyncio 模块。对于Python以前的版本,Twisted, geventtornado 都提供了类似的功能。

对于本文中刚开始的一些示例代码,出于简单易懂的原因,我并没有引入错误处理和异常的机制。在实际编码中,适当的异常处理是一个非常重要的编码习惯。在本文的最后,我将用几个例子来展示Python 3.4中的 asyncio 库是如何处理异常的。

开始:重温Hello World

我们来写一个程序解决一个简单的问题。本文后面篇幅的多个程序,都是在这题的基础之上稍作改动,来阐述协程的思想。

写一个程序每隔3秒打印“Hello World”,同时等待用户命令行的输入。用户每输入一个自然数n,就计算并打印斐波那契函数的值F(n),之后继续等待下一个输入

有这样一个情况:在用户输入到一半的时候有可能就打印了“Hello World!”,不过这个case并不重要,不考虑它。

对于熟悉Node.js和JavaScript的同学可能很快能写出类似下面的程序: