感谢博主lyrebing  博文地址:http://blog.csdn.net/lyrebing/article/details/20446061

1.  Actor用法

1.1 Actor的基本使用

Scala会建立一个线程池共所有Actor来使用。

receive模型是Actor从池中取一个线程一直使用;

react模型是Actor从池中取一个线程用完给其他Actor用

例1-1 基本运行方式1

//actor是一个类似线程的实体,它有一个用来接收消息的信箱。
//实现actor的方法是继承Scala.actors.Actor并完成其act方法
//通过调用actor的start方法来启动它
class SillyActor extends Actor{
def act(){
for(i<- 1 to 5){
println("运行次数:"+i)
Thread.sleep(1000);
}
}
} object SimpleActor{
def main(args:Array[String]):Unit = { val sactor1 = new SillyActor();
sactor1.start(); //启动actor
}
}

运行结果:

运行次数: 1
运行次数: 2
运行次数: 3
运行次数: 4
运行次数: 5

例1-2: 基本运行方式2

object SimpleActor{
def main(args:Array[String]):Unit = {
//另一种方法:使用Actor中名为actor工具的方法来创建actor
//actor在定义后立即启动,无需在调用start()
val sactor2 = Actor.actor{
for(i<-1 to 5){
println("Another Actor"+i)
Thread.sleep(1000);
}
}
}
}

运行结果:

Another Actor1
Another Actor2
Another Actor3
Another Actor4
Another Actor5

1.2 发送消息

提示:

!

发送异步消息,没有返回值。

!?

发送同步消息,等待返回值。(会阻塞发送消息语句所在的线程)

!!

发送异步消息,返回值是 Future[Any]。

?

不带参数。查看 mailbox 中的下一条消息。

2.  接收消息

2.1  方式1:接受receive

特点:要反复处理消息,receive外层用while(..), 不用的话只处理一次。

例2-1: 使用receive接收、处理消息

//通过调用Actor.receive来接收消息
//actor发送消息时,它并不会阻塞,当actor接收消息时,它也不会被打断。
//发送的消息在接收actor的邮箱中等待处理,知道actor调用receive方法 val sactor3 = Actor.actor{
var work = true;
while(true){
Actor.receive{
case x :Int =>println("got an Int: "+x)
case _ =>println("not an Int");
}
}
} sactor3 ! 12
sactor3 ! 13
sactor3 ! 1.5

运行结果:

got an Int: 12
got an Int: 13
not an Int

2.2  方式2 接受react

特点:

(1) 从不返回;
(2) 要反复执行消息处理,react外层用loop,不能用while(..);
(3) 通过复用线程,比receive更高效,应尽可能使用react;

例2-2: 获得并显示IP网站的地址

ReactActor.java

object ReactActor{
def main(args:Array[String]):Unit={
NameResolver.start();
NameResolver ! ("www.baidu.com",Actor.self)
NameResolver ! "msg1";
NameResolver ! "EXIT"
NameResolver ! "msg2";//已经结束,不会显示
}
} object NameResolver extends Actor{
def act(){
loop{
react{
case (name:String, actor:Actor)=>
println(getIp(name))
case "EXIT" =>
println("Name resorver exit!");
exit; //跳出loop
case msg =>
println("Unhandled message" + msg);
}
}
}
/* //不使用loop的方法
def act(){
react{
case (name:String, actor:Actor)=>
println(getIp(name))
//actor ! getIp(name);
act(); //再次调用act函数
case "EXIT" =>
println("Name resorver exit!");
case msg =>
println("Unhandled message" + msg);
act();
}
}
*/ //获取IP地址
def getIp(name:String):Option[InetAddress]={
try{
Some(InetAddress.getByName(name))
}catch{
case _:UnknownHostException =>None
}
}
}

运行结果:

Some(www.baidu.com/119.75.217.109)
Unhandled messagemsg1
Name resorver exit!

3. 良好的Actor风格

3.1 Actor不应阻塞

编写良好的actor在处理消息时不应阻塞。阻塞的问题是,在actor阻塞时,另一个actor可能会对其发起另一个它能够处理的请求。如果actor在首个请求时阻塞了,那么它将不会留意到第二个请求。最坏的情形是可能带来死锁,多个actor都在等待另一个阻塞的actor的响应。

actor{
Thread.sleep(time)
mainActor ! "WAKEUP"
}

这个助手actor的确阻塞了,但由于它永远不会受到消息,因此在这种情况下是可以的。主actor可以继续响应新的请求,下面程序emoteLater方法展示了这种处理方式的用法。它创建一个新的actor来执行sleep以便主actor不阻塞,以确保它向正确的actor发送"Emote"消息,我们必须小心地在主actor中对self求值而不是在助手actor中。

object SimpleActor2Copy{

  def emoteLater(){ //辅助actor,用于休眠
val mainActor = Actor.self;
Actor.actor{
Thread.sleep(1000);
mainActor ! "Emote"
}
} def main(args:Array[String]):Unit={
val sActor2 = Actor.actor{
var emoted = 0;
var con = true;
emoteLater(); //启动一次辅助actor
Actor.loopWhile(con){//Actor.loop用来重复执行一个代码块 ,loopWhile可以加上判断条件
Actor.react{
case "Emote" =>
println("I am acting "+emoted)
emoted+=1;
if(emoted<5){
emoteLater();
}else{
con = false
} case msg =>
println("received" + msg)
}
}
}
}
}

运行结果:

I am acting 0
I am acting 1
I am acting 2
I am acting 3
I am acting 4

由于这个actor并不在sleep方法中阻塞--它的助手actor会阻塞--他可以在等待下次表演之前继续做其他事。

3.2 actor之间只通过消息进行通信。

actor模型让我们写多线程程序时只用关注各个独立的单线程程序(actor),他们之间通过消息来通讯。例如,如果BadActor中有一个GoodActor的引用:

class BadActor(a:GoodActor) extends Actor {...}

那在BadActor中即可以通过该引用来直接调用GoodActor的方法,也可以通过“!”来传递消息。选择后者!因为一旦BadActor通过引用读取GoodActor实例的私有数据,而这些数据可能正被其他线程改写值,结果就避免不了“共享数据-锁”模型中的麻烦事:即必须保证BadActor线程读取GoodActor的私有数据时,GoodActor线程在这块成为“共享数据”的操作上加锁。GoodActor只要有了共享数据,就必须来加锁防范竞用冲突和死锁,你又得从actor模型退回到“共享数据-锁”模型(注:actor对消息是顺序处理的,本来不用考虑共享数据)。

3.3 采用不可变消息

由于Scala的actor模型提供了在每个actor的act 方法中的单线程环境,不必担心在这个方法的实现中使用的对象是否是线程安全的。
每个act方法实际上被局限在一个线程中,在act方法中你可以尽情使用非同步、可变对象,actor模型被称作share-nothing的模型,因为数据局限于一个线程中,而不是被多个线程共享。
有一个例外:用于在actor间发送消息的对象中的数据由多个actor“共享”。这时要关注消息对象是否安全。

保证消息对象线程安全的最好方法就是保证只使用不可变对象作为消息对象。消息类中只定义val字段,且只能指向不可变对象。定义这种不可变消息类的简单方法就是使用case class, 并保证其所有的val字段都是不可变的。Scala API中提供了很多不可变对象可用,例如基本类型、String、Tuple、List,不可变Set、不可变Map等。

如果你发现确实需要把一个可变对象obj1发送给其他actor,也因该是发送一份拷贝对象obj1.clone过去,而不是把obj1直接发过去。例如,数据对象Array是可变且未做同步的,所以Array只应该由一个actor同时存取,如果需要发送数组arr,就发送arr.clone(arr中的元素也应该是不可变对象),或者直接发送一个不可变对象arr.toList更好。

总结:大部分时候使用不可变对象很方便,不可变对象是并行系统的曙光,它们是易使用、低风险的线程安全对象。当你将来要设计一个和并行相关的程序时,无论是否使用actor,都应该尽量使用不可变的数据结构。

3.4 让消息自说明

对每一种消息创建一个对应的case class,而不是使用上面的tuple数据结构。虽然这种包装在很多情况下并非必须,但该做法能使actor程序易于理解,例如:

// 不易理解,因为传递的是个一般的字符串,很难指出那个actor来响应这个消息
lookerUpper ! ("www.scala-lang.org", self)
// 改为如下,则指出只有react能处理LoopupIP的actor来处理:
case class LookupIP(hostname: String, requester: Actor)
lookerUpper ! LookupIP("www.scala-lang.org", self)

4. 不同JVM间的消息访问

服务器端:

object MyServer{
def main(args:Array[String]):Unit={ Actor.actor { // 创建并启动一个 actor
// 当前 actor 监听的端口: 3000
RemoteActor.alive(3000)
// 在 3000 端口注册本 actor,取名为 server1。
// 第一个参数为 actor 的标识,它以单引号开头,是 Scala 中的 Symbol 量,
// Symbol 量和字符串相似,但 Symbol 相等是基于字符串比较的。
// self 指代当前 actor (注意此处不能用 this)
RemoteActor.register('server1, Actor.self); // 收到消息后的响应
Actor.loop {
Actor.react {
case msg =>
println("server1 get: " + msg)
}
}
} }
}

客户端:

import scala.actors.Actor
import scala.actors.remote.RemoteActor
import scala.actors.remote.Node object SimpleActor2Copy{
def main(args:Array[String]):Unit={
Actor.actor {
// 取得一个节点(ip:port 唯一标识一个节点)
// Node 是个 case class,所以不需要 new
val node = Node("127.0.0.1", 3000) // 取得节点对应的 actor 代理对象
val remoteActor = RemoteActor.select(node, 'server1) // 现在 remoteActor 就和普通的 actor 一样,可以向它发送消息了!
println("-- begin to send message")
remoteActor ! "ActorClient的消息"
println("-- end") }
}
}

运行结果:

控制台输出客户端发送的消息  “server1 get: ActorClient的消息”

Scala学习笔记--Actor和并发的更多相关文章

  1. 基于.net的分布式系统限流组件 C# DataGridView绑定List对象时,利用BindingList来实现增删查改 .net中ThreadPool与Task的认识总结 C# 排序技术研究与对比 基于.net的通用内存缓存模型组件 Scala学习笔记:重要语法特性

    基于.net的分布式系统限流组件   在互联网应用中,流量洪峰是常有的事情.在应对流量洪峰时,通用的处理模式一般有排队.限流,这样可以非常直接有效的保护系统,防止系统被打爆.另外,通过限流技术手段,可 ...

  2. Scala学习笔记及与Java不同之处总结-从Java开发者角度

    Scala与Java具有很多相似之处,但又有很多不同.这里主要从一个Java开发者的角度,总结在使用Scala的过程中所面临的一些思维转变. 这里仅仅是总结了部分两种语言在开发过程中的不同,以后会陆续 ...

  3. Scala学习笔记之:tuple、array、Map

    [TOC] 本文<快学Scala>的笔记 tuple学习笔记 tuple的定义 对偶是元组(tuple)的最简单形态--元组是不同类型的值的聚集. 元组的值是通过将单个值包含在圆括号中构成 ...

  4. 机器学习(三)--- scala学习笔记

    Scala是一门多范式的编程语言,一种类似Java的编程语言,设计初衷是实现可伸缩的语言.并集成面向对象编程和函数式编程的各种特性. Spark是UC Berkeley AMP lab所开源的类Had ...

  5. Scala学习笔记之Actor多线程与线程通信的简单例子

    题目:通过子线程读取每个文件,并统计单词数,将单词数返回给主线程相加得出总单词数 package review import scala.actors.{Actor, Future} import s ...

  6. 【大数据】Scala学习笔记

    第 1 章 scala的概述1 1.1 学习sdala的原因 1 1.2 Scala语言诞生小故事 1 1.3 Scala 和 Java  以及 jvm 的关系分析图 2 1.4 Scala语言的特点 ...

  7. scala学习笔记

    一 入门 为了增加编程趣味和技能,学习新语言,体会函数式编程和简易的并发管理模型,了解日渐活跃的Spark,尝试下Scala.Scala = Scalable language,作者是Martin O ...

  8. 原创:Scala学习笔记(不断更新)

    Scala是一种函数式语言和面向对象语言结合的新语言,本笔记中就零散记下学习scala的一些心得,主要侧重函数式编程方面. 1. 以递归为核心控制结构. 实现循环处理的方式有三种:goto,for/w ...

  9. Scala学习笔记 & 一些不错的学习材料 & 函数编程的历史八卦

    参考这篇文章: http://www.ibm.com/developerworks/cn/java/j-lo-funinscala1/ 这也是一个系列 严格意义上的编程范式分为:命令式编程(Imper ...

随机推荐

  1. ANTLR

    http://dreamhead.blogbus.com/logs/10756716.html

  2. 完整的开发一个ContentProvider步骤

    1.定义自己的ContentProvider类,该类需要继承Android提供的ContentProvider基类.2.向Android系统注册这个"网站",也就是在Android ...

  3. 【HDOJ】1224 Free DIY Tour

    DP. #include <cstdio> #include <cstring> #include <cstdlib> #include <algorithm ...

  4. 用QtWebKit开发简单的浏览器

    用QtWebKit开发简单的浏览器 1.代码实现 工程目录结构如下: AddressBar类包含了地址栏和按钮两个控件,将地址栏回车和按钮点击信号与goToSite()槽连接. 当回车和点击事件发生时 ...

  5. Delphi 调试Dll报错 通过GetLastError显示错误信息。

    LibHandle := LoadLibrary('c:\windows\system32\SpcClass.dll');  ShowMessage(SysErrorMessage(GetLastEr ...

  6. cf298F:状压dp+剪枝

    div2的F题,只想到了一个复杂度略高的dp,T了几次,后来加了剪枝减掉一些无用的状态终于过了.. 题意: 一个n*m的矩阵 (n<=5,m<=20),对格子进行黑白染色,已经给出了每行每 ...

  7. HDU-2571命运

    Problem Description 穿过幽谷意味着离大魔王lemon已经无限接近了!可谁能想到,yifenfei在斩杀了一些虾兵蟹将后,却再次面临命运大迷宫的考验,这是魔王lemon设下的又一个机 ...

  8. HDU-3661(贪心)

    Problem Description In a factory, there are N workers to finish two types of tasks (A and B). Each t ...

  9. 查看db2表空间使用率

    select char(TABLESPACE_NAME,16) tablespace_name,decimal(PAGE_SIZE/1024,4,2) page,used_pages*100/usab ...

  10. ASP.NET MVC4.0 部署

    EntifyFramework 5.0.0 安装 http://www.nuget.org/packages/EntityFramework/5.0.0 1. 文章,部署前的配置 http://www ...