本书出版之时曾被读者批评翻译问题,最受批评的地方就是此章,future翻译成了“期物”,算是一个新名词的尝试。但是读者并不买账。后面的版本,出版社干脆把不少地方的“期物”改回原文 – future。
个人看法是:不影响理解即可,宽容些吧。
future可以看成异步操作的产物,可以把它看成一个令牌。执行代码时候,因为系统不阻塞调用,并不(也做不到)立刻返回结果,而是返回一个可以用于获取未来执行结果的对象 – 令牌。将来拿这个对象获取执行结果,执行成功或者执行失败。
为什么要使用异步操作呢?很容易理解,异步操作是网络时代系统的绝配,最佳搭档。因为网络操作有延时,这种延时通常比发生操作的主机硬件操作延时大的多得多。如果调用请求耗费主机1毫秒,等网络请求的结果返回,可能过去了5秒钟。那么,网络操作比主机指令慢了5000倍。
如果程序都是顺序执行,阻塞调用,主机就要干等这个结果5秒钟,效率可想而知。当然,操作系统会安排它休眠,把时间片分给其它程序。但是作为这个应用程序,性能肯定是不够的。如果程序要执行很多次类似的调用,那么换个角度,每个调用发起之后,都不要同步等着结果,而是有了结果之后,再去拿结果。这样就可以同时发起很多的请求,在很多请求都在同时等待操作结果的一段时间内,不断的得到结果。这样的效率就高了很多倍。实际上,完全也可以用协程进行操作,操作效率也很高。
所以,异步操作对于I/O密集的操作领域是非常必要的。不过具体到不同的语言实现,操作模型、习惯用法还是不一样的。
Python本质上是同步执行的程序,从上到下的代码依次运行。如果使用异步机制,就需要额外的代码库支持了。本章主要讨论了concurrent.futures包的使用。
concurrent.futures包有两个类: ThreadPoolExecutor 和 ProcessPoolExecutor 。一个是线程池模型,一个是进程池模型。
代码例子并不难理解,自己跑一下即可。
作为对比,建议读者可以研究一下Node.js。这个以JavaScript语言为编程语言的开发平台,彻底的异步编程模式为其特色。因为有Promise库的支持,它的很多操作都可以使用future模式进行 – this->then->that链式调用。它不但解决了阻塞的时间耗费问题,更重要的是消解了异步调用时序需求造成的“回调地狱”大麻烦。
JavaScript的ES6版本中,Promise已经纳入了标准接口。