S-JIS[2011-02-26] 変更履歴

Scala Actor

Actorは、Scalaスレッドを扱うクラスの一種。


概要

Actorは、送られてきたメッセージ(オブジェクト)に対して処理を行う。
1つのActorに対して1つのスレッドが割り当てられる。
同一Actorに複数のメッセージが送られた場合、キュー(メッセージボックス)に入れられ、そこから順番に処理していく。

Actor同士は同じデータを共有することが無い為、スレッド間の競合を気にする必要が無い。
ただしメッセージとして送られたオブジェクトは共有されうるので、同一オブジェクトを複数Actorに送るような事をする場合には注意が必要。
ただ、オブジェクトが不変であれば気にする必要は無いので、不変オブジェクトが推奨されるのだろう。


Actorトレイト

普通は直接Actorトレイトを使うことは無いが、基本(実際に使用される動作)として。

Scala Java相当 備考
import scala.actors.Actor
   
class Sample extends Actor {

  def act() = {
    〜
  }
}
class Sample extends Thread {
  @Override
  public void run() {
    〜
  }
}
Actorのact()メソッドがThreadのrun()メソッドに相当する。
act()内でループしない限り、一度だけ実行して終了する。
val s = new Sample
s.start
Sample s = new Sample();
s.start();
startでスレッド実行を開始する。
s.restart
  restartで一度終了したActorを再実行できる。
class Sample extends Actor {
  def act() = {
    loop {
      react {
        case "end" => exit
        case m => println(m)
      }
    }
  }
}

val s = new Sample
s.start
s ! "hello" //メッセージ送信
  メッセージを受け取る記述。
receivereactはメッセージを受け取るまで待機する。
「!」メソッドでメッセージを送信する。

Actor関連のメソッド

Actorを扱う為のメソッドはActorオブジェクト(コンパニオンオブジェクト)にも定義されている。

import scala.actors.Actor._
メソッド 実際のイメージ 備考
actor
val a = actor {

  println("hello")
}
val a = new Actor {
  def act() = {
    println("hello")
  }
}
a.start
actorメソッドで処理内容を書くと、自動的にスレッドがstartする。
loop
val a = actor {
  loop {
    〜
  }
}
  while(true) {
    〜
  }
act()内は一度実行されると終了するので、何度も処理する為にループする必要がある。
素直にwhile式を使えばいいような気がするのだが、actorではloopというメソッドが用意されている。
loopは無限ループなので、ループ内で何らかの条件によって終了するように作らないといけない。
loopWhile
val a = actor {
  loopWhile(条件) {
    〜
  }
}
  while(条件) {
    〜
  }
loopは無限ループだが、loopWhileでは(while式の様に)ループ条件演算を指定できる。
receive
val a = actor {
  loop {
    receive {
      case "end" => exit
      case m => println(m)
    }
  }
}
  receiveでメッセージを受け取る。
receiveはメッセージが来るまで待機する。
受け取ったメッセージを(match式の様に)場合分けして処理を行う。
パターンにマッチしないメッセージがある場合はMatchErrorとはならず、メッセージボックス(キュー)に残り続ける。
reactの方がスレッドの処理効率がいいらしい。関数脳p.284)
react
val a = actor {
  loop {
    react {
      case "end" => exit
      case m => println(m)
    }
  }
}
  メッセージを受け取る。receiveより効率がいいらしい。
val a = actor {
  loop {
    {
      react {
        case "end" => exit
        case m => println(m)
      }: Unit
    } andThen {
      println("after")
    }
  }
}
  reactは戻り値を返さない(戻り型がNothingである)ので、reactブロックの後に処理を書いても実行されない。
andThenを使うとreactの後で実行する処理を記述できる。
ただしandThenはNothingのメソッドではないので(「react{ 〜 } andThen { 〜 }」のような書き方が出来ない)、Unitに変換する必要がある。
(参考: 制御構造
reactWithin
val a = actor {
  import scala.actors.TIMEOUT
  loop {
    reactWithin(5*1000) {
      case TIMEOUT =>
        println("end")
        exit
      case m =>
        println(m)
    }
  }
}
  タイムアウトありのreact
指定時間の間にメッセージが来ないと、TIMEOUTオブジェクトがメッセージとして送られてくる。
reply
val a = actor {
  loop {
    react {
      case "end" => exit
      case n:Int => reply(n*2)
      case m => println(m)
    }
  }
}
  メッセージの送信元に返信を返す。
(送信元が返信を受け取る為には、!?!!メソッドを使ってメッセージを送信する)
exit
val a = actor {
  loop {
    react {
      case "end" => exit
      case m => println(m)
    }
  }
}
  exitでアクター処理を終了する。
!
a ! "メッセージ"
  アクターにメッセージを送信するには「!」メソッドを使う。
メッセージはオブジェクトであれば何でもいい。
アクターがreplyで返信を返したとしても無視する。
!?
val r = a !? "メッセージ"
  アクターにメッセージを送信し、返信(reply)を受け取る。
返信が返ってくるまでブロックする(待機する)。
!!
val f = a !! "メッセージ"
val r = f()
  アクターにメッセージを送信し、返信を受け取る為のFutureオブジェクトを返す。
Futureのapply()メソッドを呼べば返信が取得できる。
apply()メソッドは、返信が返ってくるまでブロックする(待機する)。
メッセージを送信してから返信が返ってくるまでに別の処理を行いたい場合に使用する。
mailboxSize
val a = actor {
  loop {
    react {
      case "end" => exit
      case 'm =>
        println(mailboxSize)
      case n:Int =>
        println(n)
    }
  }
}
  mailboxSizeでメールボックス(キュー)に入っているメッセージの個数を取得できる。
(処理されなかったメッセージはメールボックスに残り続ける)
getState
a.getState
  アクターの稼動状態を取得する。

スレッドへ戻る / Scala目次へ戻る / 技術メモへ戻る
メールの送信先:ひしだま