Scalaの標準ライブラリは、Java程大きくありませんが、パーサーコンビネーターを筆頭に小粒でもぴりりと辛い逸品が揃っています。この記事でご紹介する「アクター」もその一つ、マルチスレッドのスパゲッティをおいしいペペロンチーノに変えてくれる、魔法のスパイスです。new Threadとしたくなったら、一歩立ち止まってscala.actorsパッケージの扉を叩いてみてください。
なお、本稿では2回にわたって、アクターとは何なのか、アクターを使うとどのような事が可能なのかをご紹介する予定です。並行処理に興味があったり、なんらかの形で並行処理に触れたりしたことがあれば、より楽しめると思います。
ロック不要のマルチスレッドプログラミング
I/O処理やデータベース通信のようなブロッキングが発生する処理をメインの処理とは分離して並行に扱いたい、でもスレッドを立ち上げるとロックが面倒で...そう感じているプログラマの方も多いことと思います。新しくスレッドを立ち上げると直感的に並行処理を実現できるものの、いろいろな問題が発生してしまいます。
例えば、複数スレッドからアクセスされるメモリーにはきちんとロックをかけないとデータの不整合が生じてしまいます。さらに、そのロックの順番を間違えるとデッドロックが発生してしまいますし、条件変数を使ったスレッドの休眠・再開を考慮しながらプログラムの流れを追いかけるのは困難です。しかも、もしロックの絡みから不具合が生じた場合、不具合の発生はタイミングに依存するので、テストでも再現できない場合が多く、修正が困難になりがちです。

ところが、繁雑なロックの事を全く考えなくても、かなりお手軽にマルチスレッド処理を実現できる方法があります。それが今回ご紹介する「アクター」です。アクターと呼ばれるスタイルの計算オブジェクトを立ち上げ、そのアクター同士がお互いにメッセージのやり取りをしながら全体の処理を進めていく、という方式です。このアクター方式は、
- アクター(スレッド)同士でメモリーを共有しないので、ロック操作が不要になる。
- シンプルな抽象化なのでプログラムがわかりやすく、マルチスレッドプログラムでも安全性が保てる。
という利点があります。ただし、従来の共有メモリー方式に比べてきめ細かい制御がやりにくくなるので、比較すると実行パフォーマンスは劣ります。
共有メモリー方式 | アクター方式 | |
---|---|---|
パフォーマンス | ○ | △ |
安全性 | × | ○ |
抽象度 | × | ○ |
アクター方式は、実行性能よりもプログラムをシンプルに安全に書ければうれしいとき、例えば、データベースアクセス中に「お待ちください」のアニメーションを表示しながらクエリー結果の応答を待つとか、バックグラウンドでHTTP通信を行ってデータの更新を行っておくような場合に、特にお薦めの方式です。
ちなみに、プログラミング言語Erlangが古くから(1980年代から)このアクター方式を採用し、信頼性が求められるオンライントランザクション処理の分野で大きな成果を上げています。そういう意味でも既に実績のある方式です。しかも、我らが(?)Scalaにはアクター方式を実現するためのライブラリが標準で用意されています。