Scala的诸多介绍当中,就看到了不少特别指出Scala中的Actor能够实现并行编程的强大功能,它是基于事件模型的并发机制。或者说,Scala是运用消息(message的发送、接收来实现多线程的。使用Scala能够更容易地实现多线程应用的开发。 

说到并行与消息发送、接收,我记起了上学期“并行计算”课程中的实验上机课中,在VC++6.0下使用的MPI机制就是基于消息的发送与接收来实现并行编程的。当初的实验是做得一塌糊涂啊,要不现在学习ScalaActor基于消息的并行编程机制就不会那么难理解了。可惜 

对于Java,我们都知道它的多线程实现需要对共享资源(变量、对象等)使用synchronized 关键字进行代码块同步、对象锁互斥等等。而且,常常一大块的try…catch语句块中加上wait方法、notify方法、notifyAll方法是让人很头疼的。原因就在于Java中多数使用的是可变状态的对象资源,对这些资源进行共享来实现多线程编程的话,控制好资源竞争与防止对象状态被意外修改是非常重要的,而对象状态的不变性也是较难以保证的。 

而在Scala中,我们可以通过复制不可变状态的资源(即对象,Scala中一切都是对象,连函数、方法也是)的一个副本,再基于Actor的消息发送、接收机制进行并行编程。 

下面是我刚刚学习到Actor并行编程知识所做的一个模拟程序,虽然相当简单甚至弱智(见怪不怪啊,呵呵),但总算是自己尝试着理解了《Programming Scala 铅笔书》中的内容写出来的。

 

程序目的:给定一个Int整数number,使用Actor计算从1number的总和。代码如下:

  1. import scala.actors.Actor 
  2.  
  3. def calculateSum(number: Int) = { 
  4.     val num = number 
  5.     val caller = Actor.self // 获得当前线程的引用 
  6.      
  7.     for(i <- 1 to num){ 
  8.         Actor.actor{ 
  9.             caller ! {      // 调用!发送消息 
  10.                 println(i)  // 打印每次发送出去的i 
  11.                 i   // 发送i,下面receive中case的sumInSent类型为Int 
  12.             } 
  13.         }    
  14.     } 
  15.      
  16.     // 下句的 /: 等效于List.foldLeft方法 
  17.     val sum = (0 /: (1 to num)){         
  18.         (partialSum, elem) => 
  19.             Actor.receive{  // 接收并用case匹配传入的Int值 
  20.                 case sumInSent: Int => partialSum + elem    // 统计和 
  21.             } 
  22.     }        
  23.     println("sum = " + sum) 
  24.  
  25. calculateSum(100000) 

其中的 (0 /: (1 to num)) 等效于 List 中的foldLeft 方法,/: 只不过是foldLeft方法的一个简洁写法而已。说起来List中的这个foldLeft方法也是花了我好些时间来从试错中理解到的(就是还没习惯理解英文书的讲述,得加强锻炼),特别是foldLeft方法中接下来使用Curring技巧传入参数、函数文本所代表的含义等等。我感觉Scala的语法太简洁以至于有些难理解…+_+ 

代码中已有部分注释,不过应注意importscala.actors.Actor 类,其中的self!(感叹号,发送方法)和receive方法都是Actor类中的方法。我特地不用下划线“_”导入Actor类中的所有方法,而是用显式的Actor加点的形式进行调用。 

尝试了不同的输入,程序输出如下: 

1、故意给了个超大的整数,运行中内存堆溢出了,刚好被我发现并截图了,OutOfMemoryErrorJava heap space 。有趣的是,程序继续在运行,接下来还有更加难理解的错误输出呢,输出太快来不及截图。 

 

这是“疯狂”的CPU和内存使用情况,天啊,我的内存才1 GB,它就耗掉了0.99GB+_+… 

 

       2、这个是输入为100000的运行情况,正常。

 

       其实我还不清楚上面的输出是否正常,或者说我还不知道上面的代码是否合理,因为铅笔书的下一节才是“Message Passing(消息传递)”呢。看上面的输出,很明显并不是按照从 1 100000 顺序输出的,这是不是Scala消息传递或者说并行编程的效果呢?期待接下去的学习,哈哈