Scala学习笔记11 - Actor和并发

===Actor并发

      与java的基于共享数据和锁的线程模型不同,scala的actor包则提供了另外一种不共享任何数据、依赖消息传递的模型。设计并发软件时,actor是首选的工具,因为它们能够帮助你避开死锁和争用状况,这两种情形都是在使用共享和锁模型时很容易遇到的。

 

创建actor

      actor是一个类似于线程的实体,它有一个用来接收消息的邮箱。实现actor的方法是继承scala.actors.Actor特质并完成其act方法。你可以通过actor的start方法来启动它。actor在运行时都是相互独立的。你也可以使用scala.actors.Actor对象的actor方法来创建actor,不过此时你就无需再调用start方法,因为它在创建之后马上启动。

 

发送接收消息

      Actor通过相互发送消息的方式进行通信,你可以使用“!”方法来发送消息,使用receive方法来接收消息,receive方法中包含消息处理的模式匹配(偏函数)。发送消息并不会导致actor阻塞,发送的消息在接收actor的邮箱中等待处理,直到actor调用了receive方法,如果actor调用了receive但没有模式匹配成功的消息,那么该actor将会阻塞,直到收到了匹配的消息。创建actor并发送接收消息的示例如下:

object ScalaTest extends Actor {

         defact() {

                   while(true) {

                            receive{

                                     casemsg => println(msg)

                            }

                   }

         }

        

         defmain(args: Array[String]) {

                   start()

                   this! "hello."

         }

}

 

将原生线程当作actor

      Actor子系统会管理一个或多个原生线程供自己使用。只要你用的是你显式定义的actor,就不需要关心它们和线程的对应关系是怎样的。该子系统也支持反过来的情形:即每个原生线程也可以被当作actor来使用。此时,你应该使用Actor.self方法来将当前线程作为actor来查看,也就是说可以这样使用了:Actor.self ! "message"。

 

通过重用线程获取更好的性能

      Actor是构建在普通java线程之上的,如果你想让程序尽可能高效,那么慎用线程的创建和切换就很重要了。为帮助你节约线程,scala提供了react方法,和receive一样,react带有一个偏函数,不同的是,react在找到并处理消息后并不返回(它的返回类型是Nothing),它在处理完消息之后就结束了。由于react不需要返回,故其不需要保留当前线程的调用栈。因此actor库可以在下一个被唤醒的线程中重用当前的线程。极端情况下,如果程序中所有的actor都使用react,则它们可以用单个线程实现。

由于react不返回,接收消息的消息处理器现在必须同时处理消息并执行actor所有余下的工作。通常的做法是用一个顶级的工作方法(比如act方法自身)供消息处理器在处理完消息本身之后调用。编写使用react而非receive的actor颇具挑战性,不过在性能上能够带来相当的回报。另外,actor库提供的Actor.loop函数可以重复执行一个代码块,哪怕代码调用的是react。

 

良好的actor风格

      良好的actor风格的并发编程可以使你的程序更容易调试并且减少死锁和争用状况。下面是一些actor风格编程的指导意见:

      actor不应阻塞:编写良好的actor在处理消息时并不阻塞。因为阻塞可能会导致死锁,即其他多个actor都在等待该阻塞的actor的响应。代替阻塞当前actor,你可以创建一个助手actor,该助手actor在睡眠一段时间之后发回一个消息,以告诉创建它的actor。记住一句话:会阻塞的actor不要处理消息,处理消息的actor请不要使其阻塞

      只通过消息与actor通信:Actor模型解决共享数据和锁的关键方法是提供了一个安全的空间——actor的act方法——在这里你可以顺序地思考。换个说法就是,actor让你可以像一组独立的通过异步消息传递来进行交互的单线程的程序那样编写多线程的程序。不过,这只有在消息是你的actor的唯一通信途径的前提下才成立。一旦你绕过了actor之间的消息传递机制,你就回到了共享数据和锁的模型中,所有那些你想用actor模型避开的困难又都回来了。但这并不是说你应该完全避免绕开消息传递的做法,虽然共享数据和锁要做正确很难,但也不是完全不可能。实际上scala的actor和Erlang的actor实现方式的区别之一就是,scala让你可以在同一个程序中混用actor与共享数据和锁两种模型。

      优选不可变消息:actor模型提供了每个actor的act方法的单线程环境,你无需担心它使用到的对象是否是线程安全的,因为它们都被局限于一个线程中。但例外的是在多个线程中间传送的消息对象,它被多个actor共享,因此你需要担心消息对象是否线程安全。确保消息对象是线程安全的最佳途径是在消息中只使用不可变对象。如果你发现你有一个可变对象,并且想通过消息发送给另一个actor,你应该制作并发送它的一个副本。

      让消息自包含:actor在发送请求消息之后不应阻塞,它继续做着其他事情,当收到响应消息之后,它如何解释它(也就是说它如何能记起它在发送请求消息时它在做着什么呢),这是一个问题。解决办法就是在响应消息中增加冗余信息,比如说可以把请求消息中的一些东东作为响应消息的一部分发送给请求者。
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值