Scalaで並行処理#2 – AkkaのActorを使う

2012.01.20

この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。

はじめに

前回の記事ではScala標準のActorを使用して並行処理プログラムを記述してみました。
今回はここでも少しふれたAkkaを使用してみます。

今回使用した動作環境は以下のとおりです。

  • OS : MacOS X 10.7.2
  • Java : 1.6.0_26
  • Scala : 2.9.1 final
  • SBT : 0.11.2
  • Eclipse : 3.7

実行環境のセットアップ

今回はプロジェクトの作成や実行を、ScalaのビルドツールであるSimple Build Tool(以下sbt)を使用して行います。
MacであればHomebrewで簡単にインストールできるので、インストールしましょう。
それ以外の方法でインストールするのであればこのへんをみたりすればできると思います。

% brew install sbt

サンプル用プロジェクトの作成

まず適当なディレクトリを作成し(myAkkaSampleと仮定)、build.sbtという名前で下記ファイルを用意します。
ここではプロジェクト全体の設定や依存ライブラリの設定を行います。
今回はAkkaを使用するので、その設定を記述します。
※空行も必要なので、そのとおりに入力してください

name := "My Akka Sample"

version := "1.0"

scalaVersion := "2.9.1"

resolvers += "Typesafe Repository" at "http://repo.typesafe.com/typesafe/releases/"

libraryDependencies += "se.scalablesolutions.akka" % "akka-actor" % "1.2"

libraryDependencies += "se.scalablesolutions.akka" % "akka-remote" % "1.2"

libraryDependencies += "se.scalablesolutions.akka" % "akka-stm" % "1.2"

sbtについての詳細な解説はこのあたりを参照してください。

次にソースファイルを置くディレクトリを作成します。

% cd myAkkaSample
% mkdir -p src/main/scala
% mkdir -p src/test/scala

このあと作成するファイルはsrc/main/scalaディレクトリの中に作成します。

Akka-Actorの基本

ではAkkaを使用したサンプルプログラムを作成してみましょう。src/main/scalaディレクトリにMyAkka.scalaファイルを作成し、Actorクラスを定義しましょう。

import akka.actor.Actor

class MyAkkaActor extends Actor {
  def receive = { 
    case i:Int => println("Int=" + i)
    case msg => { 
      println(msg + " by MyAkkaActor")
      self.reply("Hello," + msg.toString)
    }
  }
}

同じファイルの、MyAkkaActorの下にMainオブジェクトを記述します。
Appトレイトを実装したオブジェクトは、mainメソッドを記述しなくても起動時に内容が実行されます。

object Main extends App {
  //アクターを作成
  val myAkkaActor = Actor.actorOf[MyAkkaActor]

  //アクターを開始
  myAkkaActor.start()

  //非同期実行
  val result = myAkkaActor ! 100
  println("result = " + result)

  //同期実行
  val resultOption = myAkkaActor !! "taro" 
  resultOption match { 
	case Some(result) => println("result=" + result)
	case _ => println("None") 
  }
  
  //Futureを返す
  val resultFuture = myAkkaActor !!! "takeshi"
  resultFuture.result match { 
	case Some(result) => println("result=" + result) 
	case _ => println("None") 
  }

  //アクターを停止
  myAkkaActor.stop
}

sbtを起動して実行してみましょう。下記のような結果になるはずです。

% sbt
> run  
[info] Running Main 
result = ()
Int=100
taro by MyAkkaActor
result=Hello,taro
None
takeshi by MyAkkaActor
[success] Total time: 1 s, completed 2012/01/20 17:31:11

まずはMyAkkaActorの説明です。Scala標準のActorを使用したときとの違いは下記のようになっています。

  • scala.actors.Actorではなく、akka.actor.Actorを継承する
  • actメソッドでなく、receiveメソッドを実装する
  • loopブロックを使用していないが、loop状態になっている
  • Actor自身に定義された関数にアクセスするためにself(自身を含むActorRefのインスタンス)を使用している

そしてMyAkkaActorを使用するコード部分ですが、startでActor開始、stopでActorを停止します。
メッセージパッシングの動作において、!で非同期実行は同じですが、同期実行は!?でなく!!になっており、戻り値はOption型です。
また、以前!!で記述していた処理は、!!!で記述し、戻り値はakka.dispatch.ActorCompletableFuture型です。

それ以外に、MyAkkaActorに対してreplyを返さないケースの同期メッセージを贈った場合、(myAkkaActor !! 100)
標準のActorだとそのまま待ち続けてしまいますが、Akkaではデフォルトで5秒たつとOption[None]が返ります。

まとめ

今回はAkkaを使用してActorの基本的なプログラムを記述してみました。
Scala標準のActorと微妙に似ている箇所もあるので気をつけてください。
これ以外にもAkkaはさまざまな機能を持っていますので、次回以降ご紹介できればと思います。

参考サイトなど