Edited at

第20章:Scalaでアクター!

More than 5 years have passed since last update.

ついに アクター について語る時がきました。

Scalaの醍醐味の一つです。

そして難しいです。。。

でも、アクターを使うと並列処理を扱うことが簡単になります。

ただアクターには色々な種類があるので、そこが簡単じゃないかも。。。


アクターの歴史

2.9のアクターは知らないんだけど、2.10からは標準のscala.actorパッケージが非推奨になっていて

今後削除されるみたいなんだ。

そして、今後はAkkaのアクターを使うことが推奨されている。


Akkaとは

Typesafe社が開発していて、高い抽象度・並行・スケーラブルなアプリケーションを実装するためのScala・Javaのイベント駆動の分散並列型アプリケーションフレームワークです。

Typesafe社は、Scalaの生みの親Martin Oderskyの会社ですね。


アクターとは

ところで アクター とは何でしょうか?

アクターモデルというものがあるので、Wikipediaの定義を見てみましょう。


アクターモデル(英: actor model)とは、1973年、カール・ヒューイット、Peter Bishop、Richard Steiger が発表した並行計算の数学的モデルの一種[1]。アクターモデルでは、並行デジタル計算の汎用的基本要素として「アクター」という概念を導入している。アクターモデルは並行性の理論的理解のフレームワークとして使われるほか、並行システムの実装の理論的基礎としても利用されてきた。


そして続き。


アクターモデルの基本は「全てのものはアクターである」という哲学である。これはオブジェクト指向プログラミングにおける「全てのものはオブジェクトである」という考え方と似ているが、オブジェクト指向ソフトウェアでは基本的に逐次的に実行するのに対して、アクターモデルでは本質的に並行性を備えている点が異なる。アクターは並行的に受信するメッセージに対応した以下のような振る舞いを備えた計算実体(Computational Entity)である:

・(他の)アクターに有限個のメッセージを送信する。

・有限個の新たなアクターを生成する。

・次に受信するメッセージに対する動作を指定する。


こんなものがアクターモデルです。わかりやすいですね。

僕の理解では下記な感じとなりました。


  • オブジェクトである。そしてオブジェクト指向とそんなに変わらない。

  • 並行性を備える。これがオブジェクト指向との最大の違い。

そして別のサイトで見つけた、アクター自身の説明。


「アクター」実装は要するに、アクターと呼ばれる実行エンティティー同士でのメッセージ交換を利用して作業の調整を行う方法です (「プロセス」、「スレッド」、「マシン」といった言葉を意図的に避けていることに注意してください)。

多忙な Java 開発者のための Scala ガイド: Scala の並行性を掘り下げる


実行エンティティというのは、実体は色々とあるという話だと思います。

Javaのスレッドより簡単にconcurrentを実現できるっていう話を良く聞くと、 実体はスレッド間のやり取り のイメージを持ってしまう。だけど、スレッドという単位で決まっているわけではないし、 プロセスマシン という単位で決まっているわけでもない。

なので実体はいくつかあって、それ抽象化した概念が 実行エンティティ ってとこですかね。


メッセージ

エンティティ間を繋ぐもの。

オブジェクト指向でも、オブジェクト間を繋ぐのはメッセージですね。

とは言え、エンティティは直接メッセージをやりとりしているわけではありません。

メールボックス経由でメッセージのやりとりを行っています。


メールボックス

メッセージを入れておく箱。アクターが持っています。


定義みたいの書いてみたけど

やっぱりスレッド間通信が簡単にできるというイメージが、最初はわかりやすいと思う。


イメージ

最後に絵を描いてみたぞ。最小構成だから寂しいことになっているぞ。

クラス図ぽくしているから。

actor.png

Clientは、アクターにメッセージを送信する側だ。

Actorは、今まで出てきたアクターだ。

messageでやりとりするんだ。


アクターを使う

早速ソースコードを書いてみよう!

今回はakka-actor_2.10-2.2.3.jarを使ってみます。


プロジェクトの設定

どういうやり方にしようか迷ったんですけど、Gradleで実行する体で記述していきます。

sbt使っている人は、自分で設定してね!


Gradleのインストール

まず、Gradleのインストールから。

Gradleのインストールはこちらを参考にして下さい。


プロジェクト作成

次に、プロジェクト作成なんだけど、今回のソースコードはいつものgistじゃなくてリポジトリを作ったよ。

以下でcloneしてね。

$ git clone https://github.com/f81/actor_sample.git

構成は以下な感じとなっています。

actor_sample $ tree

.
├── LICENSE
├── README.md
├── build
├── build.gradle
└── src
├── main
│ ├── resources
│ └── scala
│ └── ActorSample.scala
└── test
├── resources
└── scala


ソースコード

準備が色々大変だったけど、ようやくソースコードだ。


ActorSample.scala

import akka.actor.ActorSystem

import akka.actor.Props
import akka.actor.Actor
import akka.routing.RoundRobinRouter

object ActorSample {

def main(args: Array[String]): Unit = {
val system = ActorSystem("helloSystem")
val router = system.actorOf(Props[HelloActor].withRouter(RoundRobinRouter(4)))

router ! "Hello"

system.shutdown
}
}

class HelloActor extends Actor {

def receive = {
case "Hello" => println("Hello World!!")
case _ => println("Bad World!!")
}

}



Gradleで実行!

ソースコードを説明するよりも、とりあえず動かしちゃおう!

$ cd actor_sample

$ gradle run
:compileJava UP-TO-DATE
:compileScala
:processResources UP-TO-DATE
:classes
:run
Hello World!!

BUILD SUCCESSFUL

Total time: 11.639 secs

:runのあとにでてきたHello World!!が出力させたい結果だ!


説明

このソースでの登場人物は2人。ActorSampleオブジェクトとHelloActor。

先にアクターのHelloActorから。


HelloActor

akka.actor.Actorを継承しています。

メッセージをreceiveで受け取ると、メッセージが何であるかを判断し、何かをします。

ここでは以下のようにしています。


  • メッセージで文字列Helloを受け取った場合は、Hello World!!とコンソール出力

  • メッセージで文字列Hello以外を受け取った場合は、Bad World!!とコンソール出力

かなり簡素に用意しましたが、これがアクターです。


ActorSample

今回の実行時のメインクラスとなります。

ここでやりたいことは、アクターにメッセージを送ることです。その箇所は

router ! "Hello"

となります。

ここのrouterは、その前で定義しています。

val system = ActorSystem("helloSystem")

val router = system.actorOf(Props[HelloActor].withRouter(RoundRobinRouter(4)))

ActorSystemは、アクター全体を管理するコンテナオブジェクトのようなモノだと今回は説明させてください。

こいつをsysytemという名前にしています。

そしてsystemから、HelloActorインスタンスを管理するrouterを取得します。

僕はrouterを、オブジェクトプールと捉えています。

今回はRoundRobinとしてrouterを用意していますが、他にも種類があります。

HelloActorと、routerの出どこはわかったかな?

HelloActorrouterにメッセージを渡すと、HelloActorが動くんだ。

そしてメッセージを渡すと着に使うのが!です。

!はもちろんメソッドです。

更に、アクターの戻り値を必要としないことも言っています。

アクターの戻り値を必要とする場合は?それはまた別の機会にします。

これでakkaのアクターの一番最初のところ見ることができたかな?


まとめ

Javaでスレッドを扱うのと較べてスッキリ書ける印象を持てたね。

今回はアクターの初歩のところを語ってみた。

Akkaはフレームワークというくらいに大きなモノなので、見切れていないし今回ほとんど語れていないです。

進化が止まらないAkkaをもっと使ってみようぜ!


次回からについて

本20章をもって、「やってみよう Scala!基礎編」は終了です。

読んでいただいた方、コメントをいただいた方、ストックしていただいた方、

皆様ありがとうございました!

それでは、さようなら。富林寺先生の次回作にご期待ください!

という打ち切り宣言ではなく、

続けて次回から「やってみよう Scala!応用編」をはじめます。

Scala関連日本語情報の充実に微力ながら貢献できるよう、引き続き投稿を続けますのでよろしくお願いいたします!