Paul Butcher是一位资深程序员,涉猎广泛,从单片机编码到高级声明式编程无所不精,现在他开办了独立咨询公司Ten Tenths。他曾任SwiftKey的首席软件架构师,并先后担任Texperts和Smartner的CTO。他从1989年开始攻读博士学位,在并行计算和分布式计算的领域深造,当时他便深信并发编程将成为主流。二十年后,他的观点终于得以验证——整个世界都在讨论多核以及如何发挥其优势。Paul Butcher的著作《七周七并发模型》延续了《七周七语言》的写作风格,通过七个精选的模型帮助读者了解并发领域的轮廓。除《七周七并发模型》外,Paul还著有在亚马逊获得全五星好评的《软件调试修炼之道》

问:你在很久以前就成为了一位程序员,是什么激发了你学习并行和分布式计算的兴趣?

确实是很久以前了——我的第一个程序是在第一代可编程计算器上编写的,那时我才10岁。

激发我兴趣的是编程语言。当我在1989年攻读博士的时候,我想找一个存在待解决难题的领域,所以我决定研究并行和分布式计算的语言。这是一个很有趣的领域,我的选择没有错,但是我错误地估计了该领域成为主流所需要的时间。

在获得博士学位之后,我很幸运有机会能在一个大型的共享内存多线程系统上工作(一个并行PostScript解释器),这里的工作为我解决线程和锁这样的困难问题提供了一个很好的基础。

问:和传统串行模型比较,并发模型有哪些优势?应用并发模型的最佳场景是什么?

我们必须要接受非串行(并行或并发)编程,这样才能有效地使用当今的多核处理器。但是并发不仅在利用多核上有用武之地(当然,必须要使用正确),并发还会让软件变得更具响应性,它比串行软件更易写,也更好理解。

或许并发最有魅力的地方在于它的容错性。串行软件永远不可能像并发软件那样具有弹性(比如,当你运行串行代码的硬件设备失效了会怎样?)

问:是否有可能对比不同并发模型的性能?

评量两种编程语言的基准是很困难的,所以评量两种并发模型的基准也是很困难的。性能所依赖的因素很多(硬件架构,所执行的算法的本质,通信在网络上进行还是在本地运行的进程间进行,等等)所以要想得到概括的结论几乎是不可能的。

不同的方法肯定有不同的“有效点”。比如,如果你在搞数值计算,在GPGPU上运行数据平行代码就会得到奇佳的性能。如果你的数据是太字节级别的,那么Lambda架构就是最棒的。

如果要说多用途编程的话,在我心中关于方式的选择其实无关于性能,我更看重的是这种方式是否契合我的思维模型,是否能提供我所需要的设备。再比如说,如果你需要对于分布和容错的支持,角色(Actors)差不多就是这种情况下唯一的选择了。

问:多个逻辑上的并发程序执行时,哪种方式能实现最大的效率,独立执行还是串行执行?

就像上一个问题中所说的那样,这个答案取决于你所处的具体环境。如果你所说的“最大效率”意味着利用多核,那么从定义上说串行执行的效率是比较低的,因为它只能利用一个核。

所以我猜你想问的是在单核上的效率。总体来说,并发确实会带来一些开销,但是今天我们已经有了优化充分的运行时间,所以开销其实是很小的。Erlang的进程,Go的goroutines,Clojure的core.async,以及其他语言中相似的机制可以让多个逻辑上的并发程序的执行效率相当高。

除了非常少的几个特殊领域之外,我们已经过了因为效率而放弃并发的阶段了。

问:Erlang和Go受到了CSP模型的影响,但是Process Algebra领域有三个分支:CSP,ACP和CCS。为什么还没有受ACP和CCS影响的新语言出现?

Go肯定是受CSP影响的语言。相比于CSP,Erlang和角色模型(Actor模型)之间的共同点更多(虽然角色模型和进程演算肯定是相互影响的)。

有趣的是,Erlang的设计者们在设计这个语言的时候从没听说过角色模型,我认为这一点就是这个问题的答案——在软件领域,学界和业界之间的纽带并不强,语言设计通常都由实用主义驱动,而非理论。

问:不同的语言有着不同的并发模型,而且交集比较少。如果不同的子系统要使用不同的并发模型,那么这个系统很有可能要使用两种以上的语言,那么如何才能解决多语言调试问题?比如有基于ErlangVM和JVM的子系统,两个子系统之间的连接方式就是进程间通信。在外部高并发的情况下,负载就汇聚到了两个子系统的边界上,这时有什么好的解决方法?

这两个问题都很难,没有简单的答案。哪怕在最好的情况下,多语言程序都是很具有挑战性的,引入不同的并发模型只会让情况更加复杂。

唯一的解决方案就是合理的高层设计。你需要用清晰的原则架构你的系统,比如用最大化内聚和最小化耦合的方式来减少子系统间的通信,所以相比于子系统内部通信,系统间通信更少。

问:Erlang和Go都没有完整的类型系统,所以数据的有效性处理比较麻烦,比如传递结构化的json数据时。关于如何在Channel传递结构化的数据,你是否有一些建议?

关于静态类型和动态类型的争论几乎和编程一样古老,而且这不仅仅是并发领域独有的问题。

当我在使用动态类型的系统时(比如Erlang),我会用同一种方式来告诉自己,当我在做串行代码的时候并发代码是正确的,这个方法就是做很多测试。

问:Erlang比Scala古老,但最近Scala相关的技术很流行,效率也很高。Scala的Actor并发模型有可能取代Erlang吗?

现在下结论还为时尚早。Scala的Akka确实让人印象深刻,无论什么时候我都会毫不犹豫地推荐它。但是Elixir(以Erlang VM为目标的新语言)很大程度上重新点燃了我对Erlang生态系统的兴趣。我们非常幸运,可以从两个很好的选择中挑选。在可以预见的未来,相信两种技术都会保持流行的态势。

问:编写并发/并行程序要比一般的串行程序困难得多,有没有什么方法可以降低这种难度? 有没有哪些适合并发编程的思考模型可以推荐给读者们?

可以肯定的是带有线程和锁的并发编程确实很难。比难更糟的是,你几乎无法确定一个基于线程和锁的程序是否正确。

但是如果你选择了正确的工具,就会避免很多问题。在很多情况下,并发解决方案可以比相对应的串行解决方案更加简单、更加容易。

或许我能给你的最好建议,就是尽量多地了解并发的不同方式,这样你才能知道什么是可用的。你的工具箱越大,就越有可能为你手头的问题找到合适的工具。


更多精彩,加入图灵访谈微信!