![]() |
![]() |
![]() |
アクターのトレイト 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 | 目次 |