从Scala的诸多介绍当中,就看到了不少特别指出Scala中的Actor能够实现并行编程的强大功能,它是基于事件模型的并发机制。或者说,Scala是运用消息(message)的发送、接收来实现多线程的。使用Scala能够更容易地实现多线程应用的开发。
说到并行与消息发送、接收,我记起了上学期“并行计算”课程中的实验上机课中,在VC++6.0下使用的MPI机制就是基于消息的发送与接收来实现并行编程的。当初的实验是做得一塌糊涂啊,要不现在学习Scala的Actor基于消息的并行编程机制就不会那么难理解了。可惜…
对于Java,我们都知道它的多线程实现需要对共享资源(变量、对象等)使用synchronized 关键字进行代码块同步、对象锁互斥等等。而且,常常一大块的try…catch语句块中加上wait方法、notify方法、notifyAll方法是让人很头疼的。原因就在于Java中多数使用的是可变状态的对象资源,对这些资源进行共享来实现多线程编程的话,控制好资源竞争与防止对象状态被意外修改是非常重要的,而对象状态的不变性也是较难以保证的。
而在Scala中,我们可以通过复制不可变状态的资源(即对象,Scala中一切都是对象,连函数、方法也是)的一个副本,再基于Actor的消息发送、接收机制进行并行编程。
下面是我刚刚学习到Actor并行编程知识所做的一个模拟程序,虽然相当简单甚至弱智(见怪不怪啊,呵呵…),但总算是自己尝试着理解了《Programming Scala 铅笔书》中的内容写出来的。
程序目的:给定一个Int整数number,使用Actor计算从1到number的总和。代码如下:
- import scala.actors.Actor
- def calculateSum(number: Int) = {
- val num = number
- val caller = Actor.self // 获得当前线程的引用
- for(i <- 1 to num){
- Actor.actor{
- caller ! { // 调用!发送消息
- println(i) // 打印每次发送出去的i
- i // 发送i,下面receive中case的sumInSent类型为Int
- }
- }
- }
- // 下句的 /: 等效于List.foldLeft方法
- val sum = (0 /: (1 to num)){
- (partialSum, elem) =>
- Actor.receive{ // 接收并用case匹配传入的Int值
- case sumInSent: Int => partialSum + elem // 统计和
- }
- }
- println("sum = " + sum)
- }
- calculateSum(100000)
其中的 (0 /: (1 to num)) 等效于 List 中的foldLeft 方法,/: 只不过是foldLeft方法的一个简洁写法而已。说起来List中的这个foldLeft方法也是花了我好些时间来从试错中理解到的(就是还没习惯理解英文书的讲述,得加强锻炼),特别是foldLeft方法中接下来使用Curring技巧传入参数、函数文本所代表的含义等等。我感觉Scala的语法太简洁以至于有些难理解…+_+
代码中已有部分注释,不过应注意import进scala.actors.Actor 类,其中的self、!(感叹号,发送方法)和receive方法都是Actor类中的方法。我特地不用下划线“_”导入Actor类中的所有方法,而是用显式的Actor加点的形式进行调用。
尝试了不同的输入,程序输出如下:
1、故意给了个超大的整数,运行中内存堆溢出了,刚好被我发现并截图了,OutOfMemoryError:Java heap space 。有趣的是,程序继续在运行,接下来还有更加难理解的错误输出呢,输出太快来不及截图。
这是“疯狂”的CPU和内存使用情况,天啊,我的内存才1 GB,它就耗掉了0.99GB,+_+…
2、这个是输入为100000的运行情况,正常。
其实我还不清楚上面的输出是否正常,或者说我还不知道上面的代码是否合理,因为铅笔书的下一节才是“Message Passing(消息传递)”呢。看上面的输出,很明显并不是按照从 1 到100000顺序输出的,这是不是Scala消息传递或者说并行编程的效果呢?期待接下去的学习,哈哈…
本文出自 “蚂蚁” 博客,请务必保留此出处http://haolloyin.blog.51cto.com/1177454/391310