博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Scala.Actor实践心得与设计思想
阅读量:6902 次
发布时间:2019-06-27

本文共 6144 字,大约阅读时间需要 20 分钟。

            这段时间系统的学习了Scala,编程思想上可谓收获不少,想从里面挑些值得写的东西分享给大家,Scala的Actor

可谓这门语言的一个亮点,函数式编程核心价值在于多核编程,所以就打算说说这个Actor,总结一下学习心得。先很俗

套的简单谈谈概念,然后会拿四个例子做补充。主要内容包括基本原理,设计思想,单机环境并发测试。

 

            Actor是一种基于事件的轻量级线程,在以前的并发模型中,我们需要关注共享的数据结构,而使用Actor则需要

关注操作数据的代码结构,因为减少了数据的共享。Actor的主要能力来源于消息传递,而不是采用阻塞调用的处理形式。

如果创建直接或间接扩展 Actor的类,要确保对对象的所有调用都通过消息传递进行。

 

     我把Actor的设计思想归为两类,使用目的归为两类。从设计思想上来说Scala推荐的是以消息传递为核心的设计

思想,由于Scala可以无缝使用Java类库,所以也可以采用以共享数据为核心的设计,当然也可以写出混合式设计风格的

。使用目的主要有两种,一种是Scala提供APIJava调用,另一种就是Scala自给自足。举三个例子,例子很简单,是一

个累加器。

 

1、以消息传递为核心的设计:使用Actoractor方法,使用不可变对象,不考虑数据共享问题,以消息传递为设计核心。

 

 1 
import
 actors._, Actor._
 2 
/*
 3 
 * Author:ShiYang
 4 
 * Blog:
http://shiyangxt.cnblogs.com
 5 
 * 
*/
 6 
object SendMessageStyle {
 7 
 8 
  def main(args: Array[String]): Unit 
=
 {
 9 
    val caller 
=
 self
10 
    val accumulator 
=
 actor {
11 
      var 
continue
 
=
 
true
12 
      var sum 
=
 
0
13 
      loopWhile(
continue
) {
14 
        reactWithin(
500
) {
15 
          
case
 number: Int 
=>
 sum 
+=
 number
16 
          
case
 TIMEOUT 
=>
17 
            
continue
 
=
 
false
18 
            caller 
!
 sum
19 
        }
20 
      }
21 
    }
22 
    accumulator 
!
 
1
23 
    accumulator 
!
 
2
24 
    accumulator 
!
 
3
25 
    receiveWithin(
1000
) {
26 
      
case
 result 
=>
 println(
"
Total is 
"
 
+
 result)
27 
    }
28 
  }
29 
}

 

2、以共享数据为核心的设计:构建由Actor继承共享数据操作类,以共享数据为核心。

 

 1 
import
 actors._, Actor._
 2 
 3 
/*
 4 
 * Author:ShiYang
 5 
 * Blog:
http://shiyangxt.cnblogs.com
 6 
 * 
*/
 7 
object SharedDataStyle {
 8 
  
case
 
class
 Add(number: Int)
 9 
  
case
 
class
 GetResult(sender: Actor)
10 
11 
  
class
 AddActor 
extends
 Actor {
12 
    override def act(): Unit 
=
 process(
0
)
13 
    def process(value: Int): Unit 
=
 {
14 
      reactWithin(
500
) {
15 
        
case
 Add(number) 
=>
 process(value 
+
 number)
16 
        
case
 GetResult(a) 
=>
 a 
!
 value; process(value)
17 
        
case
 _ 
=>
 process(value)
18 
      }
19 
    }
20 
  }
21 
22 
  def main(args: Array[String]): Unit 
=
 {
23 
    val addActor 
=
 
new
 AddActor
24 
    addActor.start()
25 
    addActor 
!
 Add(
1
)
26 
    addActor 
!
 Add(
2
)
27 
    addActor 
!
 Add(
3
)
28 
    addActor 
!
 GetResult(self)
29 
    receiveWithin(
1000
) {
30 
      
case
 result 
=>
 println(
"
Total is 
"
 
+
 result)
31 
    }
32 
  }
33 
}

 

3、以API形式提供给Java程序使用:由于Java不能直接向Actor发消息,所以需要对Scala!()方法进行封装。

 

 1 
import
 actors._, Actor._
 2 
/*
 3 
 * Author:ShiYang
 4 
 * Blog:
http://shiyangxt.cnblogs.com
 5 
 * 
*/
 6 
object ForJavaStyle {
 7 
  
case
 
class
 Add(number: Int)
 8 
  
case
 
class
 GetResult(sender: Actor)
 9 
  
private
 
class
 AddActor 
extends
 Actor {
10 
    override def act(): Unit 
=
 process(
0
)
11 
    def process(value: Int): Unit 
=
 {
12 
      reactWithin(
500
) {
13 
        
case
 Add(number) 
=>
 process(value 
+
 number)
14 
        
case
 GetResult(a) 
=>
 a 
!
 value; process(value)
15 
        
case
 _ 
=>
 process(value)
16 
      }
17 
    }
18 
  }
19 
  
private
 val addActor 
=
 
new
 AddActor
20 
  addActor.start()
21 
  
private
 def add(sender: Actor, num: Int): Unit 
=
 {
22 
    sender 
!
 Add(num)
23 
  }
24 
  
private
 def getResult(sender: Actor): Int 
=
 {
25 
    sender 
!
 GetResult(self)
26 
    receiveWithin(
1000
) {
27 
      
case
 result: Int 
=>
 result
28 
    }
29 
  }
30 
  def addForJava(num: Int): Unit 
=
 {
31 
    add(addActor, num)
32 
  }
33 
  def getResultForJava(): Int 
=
 {
34 
    getResult(addActor)
35 
  }
36 
}

 

Java端调用代码:

 

 1 
/*
 2 
 * Author:ShiYang
 3 
 * Blog:
http://shiyangxt.cnblogs.com
 4 
 * 
*/
 5 
public
 
class
 GetFromScala {
 6 
 7 
    
public
 
static
 
void
 main(String[] args) {
 8 
        ForJavaStyle$.MODULE$.addForJava(
1
);
 9 
        ForJavaStyle$.MODULE$.addForJava(
2
);
10 
        ForJavaStyle$.MODULE$.addForJava(
3
);
11 
        System.out.println(
"
Total is 
"
12 
                
+
 ForJavaStyle$.MODULE$.getResultForJava());
13 
    }
14 
}

      通过上面的例子可见ScalaJava语言有非常大的补充,提高了生产力。为Java提供了轻松实现多核并行编程的能

力。为了进一步测试Actor的并发性能,于是做了一个简单的单机环境并发测试。程序是构建一个Actor动态有序数组,

并发创建NActor对象,为了证明这些对象全都可用,顺序从数组的第一个Actor发消息到最后一个Actor,只有当一个

Actor接收到前一个Actor发送的消息后,才向后一个Actor发送消息。当最后一个数组元素接收到消息后,再把消息从数组

尾部用同样处理过程逆序发送到数组头部。这个消息发送过程不是并发处理,是顺序处理。这里只是为了证明这些对象全都

可用。如果为了测试并发处理,可以修改程序,让每个数组元素给后一位数组元素发消息。这样就会看到输出混乱的发送信

息,因为并发是无序的。

测试环境:双核4G内存,Windows XPSun JVM1.6,单机环境,Scala版本2.9.0.1

 

测试结果:当使用Receive方法接收消息时,由于Receive会在结束任务前一直持有线程,而Scala在后台默认只给Receive

方法启动256个线程,我的程序又是顺序的发消息,而且不是临时接收器(只处理一次消息),所以Receive在这种情况下,

只有255个并发。React接收器由于不需要长期持有线程,空闲即释放线程。所以React在本程序中可以跑20w的并发,如果

简单优化一下JVM,就可以达到100w的并发量。默认React接收器后台会调用4个线程组成的线程池。如果修改程序让每个数

组元素给后一位数组元素并发的发消息,那么在不阻塞线程的情况下,Receive方法也可以达到和React一样的并发量。因为

这个测试程序是顺序发送消息,所以就没有设置超时,如果是并发环境,建议加上超时,避免线程阻塞。

 

下面是测试程序:

 

 1 
import
 actors._, Actor._, java.util._
 2 
/*
 3 
 * Author:ShiYang
 4 
 * Blog:
http://shiyangxt.cnblogs.com
 5 
 * 
*/
 6 
object ConcurrentTest {
 7 
 8 
  val actors 
=
 
new
 ArrayList[Actor]
 9 
  val length 
=
 
1000000
10 
  var startTime 
=
 System.nanoTime
11 
12 
  def main(args: Array[String]): Unit 
=
 {
13 
    
for
 (i 
<-
 
0
 to length)
14 
      actors.add(actor {
15 
        info(
"
react: 
"
 
+
 i 
+
 
"
 actor created
"
)
16 
        reactMessage
17 
      })
18 
    actors.get(
0
!
 (
0
0
)
19 
  }
20 
21 
  def info(msg: String) 
=
 println(msg 
+
 
"
 received by 
"
 
+
22 
    Thread.currentThread)
23 
24 
  def receiveMessage {
25 
    var 
continue
 
=
 
true
26 
    
while
 (
continue
) {
27 
      receive {
28 
        
case
 (id: Int, direction: Int) 
=>
29 
          sendMessage(id: Int, direction: Int)
30 
        
case
 
"
finish
"
 
=>
31 
          
continue
 
=
 
false
32 
          val endTime 
=
 System.nanoTime
33 
          println(
"
Finish, spend time:
"
 
+
34 
            (endTime 
-
 startTime) 
/
 
1000000000.0
 
+
 
"
 secs
"
)
35 
        
case
 _ 
=>
 println(
"
input error
"
)
36 
      }
37 
    }
38 
  }
39 
40 
  def reactMessage {
41 
    var 
continue
 
=
 
true
42 
    loopWhile(
continue
) {
43 
      react {
44 
        
case
 (id: Int, direction: Int) 
=>
45 
          sendMessage(id: Int, direction: Int)
46 
        
case
 
"
finish
"
 
=>
47 
          
continue
 
=
 
false
48 
          val endTime 
=
 System.nanoTime
49 
          println(
"
Finish, spend time:
"
 
+
50 
            (endTime 
-
 startTime) 
/
 
1000000000.0
 
+
 
"
 secs
"
)
51 
        
case
 _ 
=>
 println(
"
input error
"
)
52 
      }
53 
    }
54 
  }
55 
56 
  
//
direction=0->sendLatter;direction=1->sendFormer
57 
  def sendMessage(id: Int, direction: Int) {
58 
    
if
 (direction 
==
 
0
 
&&
 id 
!=
 length) {
59 
      info(
"
Actor
"
 
+
 id 
+
 
"
 send message to the Actor
"
 
+
 (id 
+
 
1
))
60 
      actors.get(id 
+
 
1
!
 (id 
+
 
1
0
)
61 
    } 
else
 
if
 (id 
!=
 
0
 
&&
 direction 
==
 
1
) {
62 
      info(
"
Actor
"
 
+
 id 
+
 
"
 send message to the Actor
"
 
+
 (id 
-
 
1
))
63 
      actors.get(id 
-
 
1
!
 (id 
-
 
1
1
)
64 
    } 
else
 
if
 (direction 
==
 
0
 
&&
 id 
==
 length) {
65 
      actors.get(length) 
!
 (length, 
1
)
66 
    } 
else
 
if
 (id 
==
 
0
 
&&
 direction 
==
 
1
) {
67 
      actors.get(
0
!
 
"
finish
"
68 
    }
69 
  }
70 
}

 

       前些天看到Scala正在努力支持.net平台,不过我觉得.net平台有F#,所以对于.net程序员来说Scala提供不了什么

附加价值。如果非要找出来这样做的优点的话,就是Scala可以同时支持两个主流平台,野心不小。如果看后有所收获,请

推荐让更多人看到,算我为Scala的推广尽了点绵薄之力。行文仓促,如果有不对的地方,欢迎指正。

 

转载于:https://www.cnblogs.com/shiyangxt/archive/2011/07/30/2121663.html

你可能感兴趣的文章
Ext4 Disk Layout-2
查看>>
原 2017/5 JavaScript基础6--- 对象
查看>>
Python 列表、元组、字典t的常用方法
查看>>
MYSQL groupby使用方法。
查看>>
如何将ppt转换成pdf
查看>>
PowerDesigner连接MySQL数据库
查看>>
文件格式转换控件LEADTOOLS ePrint Professional
查看>>
ORACLE常见的六种RMAN恢复测试
查看>>
(Portal 开发读书笔记) Personalization
查看>>
SRCNN 实验细节
查看>>
Java多线程第二节-线程的基本使用
查看>>
界面控件Essential Studio for ASP.NET Web Forms 2017 v3发布丨附下载
查看>>
教你制作属于自己的CentOS 6.4一键自动化安装ISO镜像光盘
查看>>
线上 mysql 主库配置文档
查看>>
Java web部署目录结构和web.xml作用
查看>>
负载均衡DR(direct routing)模式
查看>>
Python中list的遍历
查看>>
Linux下查看内存等硬件信息
查看>>
mysql 登录权限
查看>>
Java之内部类
查看>>