张贵宾
guibin.beijing@gmail.com
2011.10.31
在这一章中我们看看如何在并行的基于actor的程序中处理错误。Actors与顺序的Scala代码相比,提供了几种处理异常的额外方法。特别的,我们将展示actor如何处理他们抛出的但不能被其他actor处理的异常。更普遍的,我们将寻找一种actor能够监控其他actor的方式,通过这种方式能够检测到其他的actor是正常的终止了,还是抛出异常后非正常退出了。最后,我们介绍了一些概念和技术,这些概念和技术能够简单的终止基于actor程序的管理。
6.1 Simple exception handling(简单异常处理)
当一个actor在其执行体内抛出了未被处理的异常后,此actor便会终止。出现这种情况的一种可能征兆就是有其他的actor在无限制的等待这个来自已经死去的actor的消息。因为在默认情况下,终止actor不会产生任何死锁,所以找到发生错误的actor和定位错误的actor就非常耗时。
最简单看护那些由于异常抛出而默默终止的actor的方法就是提供一个全局的异常处理器,任何一个actor执行体内有异常抛出时都会调用这个异常处理器。我们可以继承Actor(或者Reactor)并重写它的exceptionHandler成员函数即可。(exceptionHandler成员函数定义在Reactor中,而Actor中的继承关系是:ReplyActor继承自Reactor,Actor继承自Reactor)在Reactor中,exceptionHandler成员函数的签名是:def exceptionHandler: PartialFunction[Exception, Unit]
object A extends Actor {
def act() {
react {
case 'hello =>
throw new Exception("Error!")
}
}
override def exceptionHandler = {
case e: Exception =>
println(e.getMessage)
}
}
正如你所看到的,exceptionHandler是一个没有参数的,且返回值是偏函数的方法,在这里它能够处理java.lang.Exception类型的异常。任何时候从actor的函数体中抛出一个异常通常都会导致actor终止运行,当抛出异常时,运行时系统会检测actor的exceptionHandler方法能否匹配到抛出的异常类型,如果能匹配到,exceptionHandler偏函数就被应用到这个异常上,调用完exceptionHandler之后,actor会正常的终止运行。
上面的代码展示了如何复写exceptionHandler方法之后让它返回一个客户化的偏函数。下面让我们使用Scala的解释shell与actor A交互看看效果:
scala> import scala.actors._
import scala.actors._
scala> object A extends Actor {
| def act() {
| react {
| case 'hello =>
| throw new Exception("Error!")
| }
| }
|
| override def exceptionHandler = {
| case e: Exception =>
| println(e.getMessage)
| }
| }
defined module A
scala> A.start
res0: scala.actors.Actor = A$@65dfb0b5
scala> A ! 'hello
scala> Error!
正像我们所期望的,exceptionHandler方法运行了,因此打印出了抛出异常上附带的消息字符串:"Error!"
使用exceptionHandler这种形式的异常处理与诸如loop这样的控制流组合器一同工作的非常好,组合器能被用于在处理了异常之后,恢复正常执行的actor。比如我们把actor A中act方法修改成如下形式:
object A extends Actor {
def act() {
var lastMsg: Option[Symbol] = None
loopWhile(lastMsg.isEmpty || lastMsg.get != 'stop) {
react {
case 'hello =>
throw new Exception("Error!")
case any: Symbol =>
println("your message: " + any)
lastMsg = Some(any)
}
}
}
override def exceptionHandler = {
case e: Exception =>
println(e.getMessage)
}
}
react方法的调用现在被包裹在了loopWhile中,loopWhile会测试最后收到的消息是否等于 'stop ,如果等于则终止actor。现在,如果actor收到一条 'hello 消息,它将抛出异常,这个异常将会被exceptionHandler处理,处理完毕异常之后actor不会终止运行,而是通过继续执行下一个loop迭代而恢复运行。这就意味着在处理完毕了异常之后,actor能够继续工作,继续准备接收更多的消息。
下面我们在Scala的命令行中测试一下:
scala> import scala.actors._
import scala.actors._
scala> object A extends Actor {
| def act() {
| var lastMsg: Option[Symbol] = None
| loopWhile(lastMsg.isEmpty || lastMsg.get != 'stop) {
| react {
| case 'hello =>
| throw new Exception("Error!")
| case any: Symbol =>
| println("your message: " + any)
| lastMsg = Some(any)
| }
| }
| }
|
| override def exceptionHandler = {
| case e: Exception =>
| println(e.getMessage)
| }
| }
defined module A
scala> A.start
res0: scala.actors.Actor = A$@52620402
scala> A ! 'hello
scala> Error!
scala> A.getState
res2: scala.actors.Actor.State.Value = Suspended
scala> A ! 'hi
your message: 'hi
scala> A ! 'stop
your message: 'stop
scala> A.getState
res5: scala.actors.Actor.State.Value = Terminated