Actorでの例外処理
Scala の Actor は、メッセージ処理中に例外が発生すると Actor 自体が終了するようになっています。
例外を処理したい場合に、今までは case を try 〜 catch で囲んでいたのですがどうもいまいちな感じでした。以下は try 〜 catch で囲んでいる例です。
actor { loop { react { case 'foo => try { ... } catch { ... } case 'bar => try { ... } catch { ... } } } }
しばらくは忘れていたのですが、ふとしたときに link を使って例外を処理する Actor を作ればよいことに気付きました。
下記の例では、1 秒後に正常終了、2 秒後に例外発生、3 秒後にプログラムを終了する(トリガになる) Actor が起動します。
import scala.actors._ import Actor._ object HandleException { def main(arguments: Array[String]): Unit = { val workers = actor { self.trapExit = true loop { react { case work: Work => val worker = new Worker(work) link(worker) worker.start case Exit(worker:Worker, reason:Throwable) => printf("[Failure]The worker(%s) died.(reason=%s)\n", worker.name, reason) case Exit(worker:Worker, reason) => printf("[Success]The worker(%s) completed.(reason=%s)\n", worker.name, reason) case 'exit => exit }} } workers ! new Work("foo", 1000, {println("[Work]foo work.")}) workers ! new Work("bar", 2000, {throw new Exception("bar fail.")}) workers ! new Work("baz", 3000, {workers ! 'exit}) } } class Work(val name: String, val delay: Int, f: => Unit) { def apply(): Unit = f } class Worker(work: Work) extends Actor { val name = work.name def act = reactWithin(work.delay) { case TIMEOUT => work() } }
実行結果は以下のとおり。
[Work]foo work.
[Success]The worker(foo) completed.(reason='normal)
[Failure]The worker(bar) died.(reason=java.lang.Exception: bar fail.)
link を使うことで、link 先の Actor が終了したときに Exit メッセージが送られてきます。これは例外によって終了してしまった場合でも同様です。
例外によって終了した場合、Exit の reason に例外のインスタンスが設定されています。上記例では、reason の型が Throwable の場合とそうでない場合とで case を分けることで、正常終了と異常終了を切り分けています。
すべては Actor というのをちょっと実感しました。例外を処理したいなら、例外を処理する Actor を間に挟めってことですね・・・