木匣子

Web/Game/Programming/Life etc.

初试 Erlang

今天翻看了一下 Erlang 程序设计 这真是一本相当有趣的书,这也是我第一次接触 COP(面向并发编程)和函数式编程,不禁被它的设计理念所打动。

函数式编程可以很直白的用数学语言来描述算法,例如经典的 qsort 在 erlang 里简单得只需要用几句话就可以写出来:

qsort([]) -> [];
qsort([Pivot|T] ->
    qsort([X||X<-T, X<Pivot])
    ++ [Pivot] ++
    qsort([X||X<-T, X>=Pivot]).

撇开效率来说,这种写代码的感觉真是酷毙了。

至于并发编程更是给力!并发是典型的人类的思维方式,不需要额外学习,与生倶来的。例如开车的时候,人的眼睛可以同时跟踪数十辆车的情况,并做出正确的反应。有些反应甚至快过你的思考,在你的大脑还没弄明白怎么回事的时候,你的肌肉组织已经完成了一些事了。

然而在多核处理器盛行的今天,大多主流程序语言还是使用顺序过程的方法在处理各种事务。即使是多线程程序,或者操作系统,也不得不考虑各种信号量和锁来处理原子操作。为了能让这些程序正确的使用多核处理能力,程序员需要非常小心的处理共享内存,编译器优化设置等等。例如 Java 的单例模式需要复杂的线程同步技术来保证饿汉模式下正确的生成类的唯一实例,以至于不得不写出冗杂恶心的程序,两个 if(INSTANCE == null) 中间放个 synchronized(Singleton.class) 就像三明治一样:

  public class Singleton {
    private static volatile Singleton INSTANCE = null;
    private Singleton() {}
    public static  Singleton getInstance() {
        if(INSTANCE == null){
             synchronized(Singleton.class){
                 if(INSTANCE == null){ 
                     INSTANCE = new Singleton();
                  }
              } 
        }
        return INSTANCE;
    }
  }

更有甚者,有些程序不管你如何优化,都难逃编译时编译器自作聪明的修改,或者是CPU调度时自作主张的优化。对此有兴趣的话,可以读一读这篇文章或者 Steve Maguire 编写的 Writing Clean Code

Erlang 的高明之处在于它不采用共享内存的模型来完成事务,所有的程序都运行在自己独自的内存空间中。这也就保证了整个体系不需要锁来维护因为共享内存可能造成的冲突。书上举了一个非常明了的例子:

  • Sue: 你好Bill,我的电话号码是123456.
  • Sue: 你听清楚了吗?
  • Bill: 当然,你的电话号码是123456.

没错,每个人都有自己的脑子,交流的时候,人们把事物记在了自己的脑子里。人与人之间不存在所谓“共享的记忆”。如果希望另人记住你,只要给他打个招乎就行了。 Erlang 正是这样工作的。这为并发带了巨大的好处。

当然,并发还有很多工作要处理,计算机通常是不可靠的,它会当机。所以当很多计算机一起工作的时候,需要有一种机制来管理这些错误异常。

想像有一个挤满人的房间,突然有人倒在地上死了。临死之际,他说“我死于心脏病”或者“我死于胃穿孔”。Erlang进程的行为就是这样的。一个进程在它消亡的时候说的可能是“我死于有人要我去除以0”,另一个说的可能是“我死于有人要我从一个空列表里取最后一条数据。”

然后有一小撮计算机的使命就是关注这些消亡的程序,并做出正确的处理。它们就像监工一样,能够动态地维护整个系统。

Erlang 真是太有意思了,最近几天想把这本书全部看完。并把里面的实例和练习做一做。