アクターのトレイト Reactor、ReplyReactor、Actor | 目次 |
Reactor
は、全てのアクターのトレイトの親トレイトだ。[訳注: Reactor
とは「反応する者」という意味。] このトレイトを継承することで、メッセージを送受信するための基本的な機能を持つアクターを定義することができる。
Reactor
の振る舞いは act
メソッドを実装することで定義される。Reactor
が start
の呼び出しによって開始されると、act
メソッドが実行される。start
メソッドは Reactor
を返すが、冪等 (idempotent) でもある。
冪等とは、既に開始されているアクターに対して start
メソッドを何回呼び出しても何の影響もないことを意味する。
Reactor
トレイトは、型パラメータ Msg
を持つが、それはアクターが受信するメッセージの型を示す。
Reactor
の !
メソッドの呼び出しは受信者にメッセージを送信する。
訳注: Scala では、全てのメソッドの呼び出しを中置記法、つまりを a.!(msg)
のように表記できる。 そのため、メソッドを a ! msg
+
や*
などの代数の演算子になぞらえて、「演算」(operation) というふうに言うことがある。
!
によるメッセージの送信は非同期 (asynchronous)、つまり送信側のアクターは相手のメッセージ受信を待たずに即時に次の命令に続行することを意味する。例えば、a !
msg
はメッセージ msg
を a
に送信する。全てのアクターはメールボックス (mailbox) を持ち、受信されたメッセージは処理されるまでそこにバッファリングされる。
Reactor
トレイトは、forward
メソッドも定義する。このメソッドは OutputChannel
より継承され、!
メソッドと同じ効果がある。 Reactor
の子トレイト、特に ReplyReactor
トレイトは、このメソッドをオーバーライドすることで暗黙の返信先 (implicit reply destination) を実現する(下記を参照)。
Reactor
は react
メソッドを用いてメッセージを受信する1。
react
は、Msg
型のメッセージがアクターのメールボックスに到着した後どのように処理されるかを定義する PartialFunction[Msg, Unit]
型の引数を受け取る。
"おはよう" という文字列を受信することを待ち、挨拶を表示するアクターの具体例を以下に示す:
react { case "おはよう" => println("あ、おはようございます") }
react
の呼び出しは戻ってこない。 そのため、メッセージの受信後に実行すべきコードがあるならば、react
に渡される部分関数の中に入るようにしなければならない。例えば、react
の呼び出しを入れ子にすることで二つのメッセージを受信できる:
react { case Get(from) => react { case Put(x) => from ! x } }
Reactor
トレイトは、react
を用いたプログラミングを簡略化する制御構造も提供する ( * を参照)。
Reactor
の実行は、act
メソッドが本文の最後まで完了することで終了する。Reactor
は exit
メソッドを用いて明示的に終了することもできる。exit
は常に例外を発生させるため、その戻り値型は Nothing
だ。 この例外は内部で用いられるため、絶対に捕捉してはいけない。
終了した Reactor
は、restart
メソッドにより再起動できる。まだ終了していない Reactor
に対して restart
を呼び出すと、IllegalStateException
が発生する。終了したアクターを再起動すると、act
メソッドが再実行される。
Reactor
は、アクターの現在の実行状態 (execution state) を Actor.State
列挙型として返す getState
メソッドを定義する。開始前のアクターは Actor.State.New
状態にある。メッセージを待たずに実行できるアクターは Actor.State.Runnable
状態にある。メッセージを待機しながら一時停止してるアクターは Actor.State.Suspended
状態にある。終了したアクターは、Actor.State.Terminated
状態にある。
exceptionHandler
メンバーは、Reactor
の全人生における例外処理を定義することを可能にする。
def exceptionHandler: PartialFunction[Exception, Unit]
exceptionHandler
は、必要に応じて例外を処理する部分関数を返す。 Reactor
の act
メソッドの本文の外へと例外が伝搬すると、 アクターが終了前に事後処理コードを実行できるように、その例外に対してこの部分関数が適用される。exceptionHandler
の可視性は protected
であることに注意。
exceptionHandler
を用いた例外処理は、react
を用いたプログラミングのための制御構造と相性がいい(* を参照)。exceptionHandler
が返す部分関数により例外が処理されると、アクターの実行は現在の継続クロージャによって続行する。具体例としては:
Loop react { case Msg(data) => if (cond) // process data else throw new Exception("データを処理できません") } }
この Reactor
が exceptionHandler
をオーバーライドすると仮定すると、react
の本文内で発生した例外が処理された後、アクターの実行は次のループ周回へと続行する。
ReplyReactor
トレイトは Reactor[Any]
を継承し、以下のメソッドを追加もしくはオーバーライドする:
!
メソッドは現アクター(送信者)への参照を取得するためにオーバーライドされている。 送信者参照 (sender reference) は、実メッセージと共に受信アクターのメールボックスに送られる。受信者は sender
メソッドにより送信者にアクセスすることができる(下記参照)。forward
メソッドは、現在処理されているメッセージの送信者 (sender) への参照を取得するためにオーバーライドされている。実メッセージと共に、この参照は現メッセージの送信者として送られる。その結果、forward
は現アクターとは別のアクターに代わってメッセージを転送することを可能とする。sender
メソッドは、現在処理されているメッセージの送信者を返す。メッセージが転送された可能性があるので、sender
は実際にメッセージを送信したアクターではない者を返すかもしれない。reply
メソッドは直前のメッセージを送信したアクターにメッセージを送信する。reply
は「同期メッセージ通信」や「フューチャ付きメッセージ通信」への返信にも使われる(下記参照)。!?
メソッドは 同期メッセージ通信 (synchronous message send) を提供する。!?
の呼び出しは、応答を受信するまで送信アクターを待機させ、受信した応答を戻り値として返す。これには二つのオーバーロードされた形態がある。2 パラメータ形は追加で(ミリ秒の)タイムアウト引数を取り、戻り値型は Any
の代わりに Option[Any]
だ。もし送信者が指定されたタイムアウト時間内に応答を受信しなければ、!?
は None
を返し、受信した場合は、応答は Some
でラッピングされる。!!
メソッドは、受信者からの応答を転送できるようにするという意味で同期メッセージ通信に似ている。しかし、送信アクターが応答を受信するまでブロックする代わりに、このメソッドは フューチャ (Future
) のインスタンスを返す。受信者からの応答が得られれば、Future
を用いてそれを取得することができる。 また、応答が入手可能かを送信者をブロックすること無く調べることができる。これには二つのオーバーロードされた形態がある。2 パラメータ形は追加で PartialFunction[Any, A]
型の引数を取る。この部分関数は受信者の応答をポストプロセスするのに使われる。つまり、!!
は応答の受信後に部分関数を適用するフューチャを返す。フューチャの結果は、このポストプロセスの結果となる。reactWithin
メソッドは任意の時間内にメッセージを受信することを可能とする。react
と比べると、これは特殊な TIMEOUT
というパターンにマッチするまでの時間をミリ秒で表す msec
というパラメータを追加で取る(TIMEOUT
は、scala.actors
パッケージ内の case object だ)。 具体例としては:
reactWithin(2000) { case Answer(text) => // process text case TIMEOUT => println("2秒以内に答無し") }
また、reactWithin
メソッドは、メールボックスに対する非ブロックアクセスを可能にする。時間を 0 ミリ秒と指定することで、マッチするメッセージがあるかメールボックスへの検索が行われる。この時点でマッチするメッセージが無ければ、TIMEOUT
パターンとマッチする。例えば、以下に特定のメッセージを優先付けて受信する具体例を示す:
reactWithin(0) { case HighPriorityMsg => // ... case TIMEOUT => react { case LowPriorityMsg => // ... } }
上記の例では、アクターは、たとえメールボックスに先着した LowPriorityMsg
があっても、 先に次の HighPriorityMsg
を処理する。アクターが LowPriorityMsg
を最初に処理するのはメールボックスに HighPriorityMsg
が無かった場合だけだ。
その他、ReplyReactor
は Actor.State.TimedSuspended
という実行状態を追加する。 reactWithin
を用いてメッセージを待機しながら一時停止してるアクターは Actor.State.TimedSuspended
状態にある。
Actor
トレイトは ReplyReactor
を継承し、以下のメンバーを追加またはオーバーライドする:
receive
は react
のように振る舞うが、戻り値を返すことができる。これは、戻り値型について多態的である型にも反映されている。
def receive[R](f: PartialFunction[Any, R]): R
しかし、アクターがメッセージを待機して一時停止している間に内部スレッドをブロックさせるため、 receive
はアクターをよりヘビーウェイトにしてしまう。receive
の呼び出しが戻ってくるまで、ブロックされたスレッドは他のアクターを実行することができない。
link
と unlink
メソッドにより、アクターが自身を他のアクターとリンクしたり、リンクを解除したりすることができる。リンクは、他のアクターを監視したり、他のアクターの終了に反応するのに用いられる。特に、 Actor トレイトの API ドキュメントで説明されているように、リンクは exit
を呼び出した時の振る舞いに影響を及ぼす。trapExit
メンバーにより、リンクしたアクターの終了に対して、その終了理由 (exit reason) に関わらず(つまり、終了理由が 'normal
かに関わらず)反応することができる。もしアクターの trapExit
メンバーが true
ならば、リンクしたアクターが終了しても、自分は終了することはない。代わりに、リンクしたアクターが終了するたびに、Exit
型のメッセージを受信するだけだ。Exit
case class には二つのメンバーがあり、from
は終了したアクターを指し、reason
は終了理由を指す。アクターの実行を終了するとき、以下の形態の exit
を呼び出すことで終了理由を明示的に指定できる:
def exit(reason: AnyRef): Nothing
'normal
シンボル以外の終了理由でアクターが終了した場合は、その終了理由ほ全てのリンクしたアクターに伝搬する。もしアクターが捕捉されなかった例外により終了した場合の終了理由は UncaughtException case class だ。
Actor
トレイトは二つの実行状態を追加する。receive
を用いてメッセージを待機しながら一時停止してるアクターは Actor.State.Blocked
状態にある。 receiveWithin
を用いてメッセージを待機しながら一時停止してるアクターは Actor.State.TimedBlocked
状態にある。
アクターのトレイト Reactor、ReplyReactor、Actor | 目次 |