コミケ告知

サークル活動の詳細は circle タグの記事へ。
2015年9月1日火曜日

Akkaのsenderは値ではない

久々に使うと忘れがちなのでメモ。

AkkaのActorは、メッセージ受信時にはその送信元を示すActorRefをsenderで取れます。メッセージを送り返したり、デバッグ時に表示させたり、何かと便利です。
def receive = {
  case x => sender ! x  // echo
}
しかしこのsender、Futureで使おうとすると望み通りの挙動をしません。表示させてみると、送信元とは違う値になっています。(deadLetters宛になる)
Future {
  sender ! myFunc(x)  // NG
}
こういうときは、一時的に別の定数に入れて使うか…
val dst = sender
Future {
  dst ! myFunc(x)  // OK
}
最後に結果を返したいだけなら、pipeToを用いるか。
Future {
  myFunc(x)
} pipeTo sender

senderとは何なのか

senderはcontext.senderを呼び出すようになっていて、さらに追っていくとActorCellの実装に辿り着きます。
  final def sender(): ActorRef = currentMessage match {
    case null                      ⇒ system.deadLetters
    case msg if msg.sender ne null ⇒ msg.sender
    case _                         ⇒ system.deadLetters
  }
これは変数ではなくメソッドであるので、sender と記述したところに制御が到達したタイミングで、初めて処理が行われます。ちなみにvar currentMessageを弄っているのも同じActorCell内で…
  final def invoke(messageHandle: Envelope): Unit = try {
    currentMessage = messageHandle
    cancelReceiveTimeout() // FIXME: leave this here???
    messageHandle.message match {
      case msg: AutoReceivedMessage ⇒ autoReceiveMessage(messageHandle)
      case msg                      ⇒ receiveMessage(msg)
    }
    currentMessage = null // reset current message after successful invocation
  }
  //  以下略…
まずcurrentMessageがセットされたのち、システム側で処理されるAutoReceivedMessage以外であれば、receiveMessageからActorのおなじみreceiveメソッドの処理へ。終わったら戻ってきてnullに戻します。(普通にnull使うんだ… 初期値が _ だからnullで統一してるんだな)
Futureの処理がどこかで走るときには、このメッセージ受信による処理駆動パスとは異なるので、 currentMessage == null であり、 sender()はdeadLettersを返すのですね。

カッコ省略のデメリット?

仕組みが分かっていない段階で誤った使い方をしてしまった原因のひとつが、引数なしメソッドのカッコが省略されていることでした。サンプルでも基本的に省略されているから、そもそも最初はメソッドだと思っていなかったよ…(´・ω・`)
記述が簡潔!の思想に走りすぎるとデメリットもありますよ、ということで。

0 件のコメント:

コメントを投稿