[AD] Scalaアプリケーションの開発・保守は合同会社ミルクソフトにお任せください
この記事ではFutureクラスを使って非同期処理をする方法を解説します。
事前準備をする
まずは以下のListBufferについて考えてみます。
val buffer: ListBuffer[String] = ListBuffer()
このbufferに対して、処理の進捗状況を文字列で書き込んでいくプログラムを想定します。
ふたつのメソッドを用意して、それぞれ同時に実行して並行に処理してみましょう。
2つのメソッドを準備する
まずは1つ目のメソッド。
def process1(): Unit = buffer += "start first process" Thread.sleep(100) buffer += "finish first process"
開始時と終了時にそれぞれ文字列をbufferに対して挿入しています。
これはすこし時間がかかる処理であるのを模して、Thread.sleepを挟んでいます。
そして2つ目のメソッド。
def process2(): Unit = buffer += "start second process" buffer += "finish second process"
こちらは即座に処理が終了するのを模して、開始直後に終了時の文字列をbufferに対して挿入します。
process1もprocess2も同期的に実行するメソッドです。
実行コンテキスト(ExecutionContext)を準備する
Futureで非同期処理を行うには、実行コンテキスト(以下、ExecutionContext)を具体的に用意する必要があります。
Scalaにおける実行コンテキストとは、特定の処理がどのように実行されるべきであるのかについてのきまりです。
ExecutionContextは、その実行コンテキストについて、実際にどのように実行されるかを定めたクラスです。
Futureを使用して処理する際には、どのようなExecutionContextにて実行するべきかを示してあげましょう。
ここでは、Scalaが標準で用意しているExecutionContextを使用することにします。
Scalaのプログラムは、特に非同期処理をしていない限り、標準のExecutionContextにおいて実行されています。
標準のExecutionContextでは、JVMが使用できるプロセッサ数まで並行に処理することができます。
つまり、もともとの処理に加えて、プロセッサ数 - 1個の処理まで並行に処理することができるわけですね。
標準のExecutionContextを使用するには、以下のようにします。
import scala.concurrent.ExecutionContext.Implicits.global
Futureに対してExecutionContextを暗黙的に渡したい場合には、後者のようにします。
Futureの各メソッドは暗黙のパラメータとしてExecutionContextをとります。
したがって後者のようにすると毎回ExecutionContextを渡す手間が省けて、コードを簡略化することができます。
また、Futureを使用する都度明示的にExecutionContextを渡したい場合には、前者のようにすればOKです。
import scala.concurrent.ExecutionContext.global
非同期処理を実行する
では実際に非同期処理を実行してみましょう。
先述のメソッド、process1とprocess2を同時に実行するとどうなるかを見てみましょう。
以下のようにメソッドをFuture( )で囲むと、非同期処理として実行することができます。
val f1 = Future(process1()) val f2 = Future(process2())
process1は非同期処理として即座に実行され、直後にprocess2も実行されます。
したがって、実質的に同時に実行したことになります。
process1を先に開始しましたが、process1は時間がかかるため、process2の方が先に終わると期待されます。
それでは、bufferの中身を見てみましょう。
以下のようにするとf1とf2が完了した状態のbufferを返すことができます。
for { _ <- f1 _ <- f2 } yield buffer
bufferの中身は以下のようになっています。
ListBuffer(start first process, start second process, finish second process, finish first process)
実際にprocess1の開始、process2の開始、process2の完了、process1の完了という順番になっていることがわかりますね。