読者です 読者をやめる 読者になる 読者になる

the industrial

都内で働くITエンジニアの日記

ScalaのFutureについて

scala program

f:id:omiend:20150317205733p:plain

前回、理解の薄いimplicitについて学習しブログに書いたのだけど、今回は使ったことの無いScalaのFutureについて学習したので、メモがてらここに残しておく。

何故Futureをキチンと扱えるようになりたかったのかと言うと、先日参加した勉強会でFutureの話が結構出てたから。

www.slideshare.net

リクエストの多いWebアプリだと、基本Futureで非同期処理させるのが良いっぽい、という理解。

もちろんすべてを網羅しているわけではないし、基本的な事しか書いてないと思うので、そこはご愛嬌。

Version

試したScalaのVersionは、今日も2.11.4デス。

準備

必要なimportはこの二つ。

import scala.concurrent.Future
import scala.concurrent.ExecutionContext.Implicits.global

FutureクラスはFutureを使うため(当たり前w)。

globalについてはこちらを参照させていただきました。とても分かりやすい!

mashi.hatenablog.com

基本

val future: Future[Unit] = Future {
  // 非同期処理
}

こんな感じで定義すると、Future内の処理が非同期で実行されます。

非同期化する処理

非同期処理化前の処理として、こんな処理を定義。

単純に、変数resultの値に1と20を加算しているだけ。

var result: Int = 0

result += 1
println(s"result = ${result}")
result += 20
println(s"result = ${result}")

// 実行結果
// result = 1
// result = 21

実行結果の確認

非同期処理を確かめる方法として、どっかで見た下記の様な末尾回帰関数を定義。

「Future.value」メソッドでOption型が返却され、処理が完了している場合Someが、完了していない場合はNoneが返却される。

Someの中身は、定義したFutureの戻り値が格納されており、この戻り値の型に併せて、ジェネリクスを定義する(ここでは[String] ← この認識であってるかちょっと不安)。

ちなみに、「@tailrec」は末尾再帰になっていない場合コンパイルエラーにしてくれるアノテーション

import scala.annotation.tailrec
@tailrec
def checkComplete(future: Future[String]): Unit = {
  future.value match {
    case Some(s) => println(s)
    case _       => checkComplete(future)
  }
}

非同期化

この加算する処理をそれぞれ非同期処理する為に、二つのFutureでくるむ(くるむって表現あってるのか?)。

ちなみに、「Thread.currentThread.getId」は、定義した箇所が処理された時のスレッドIDを取得出来る。

var result: Int = 0

println(s"[Main] Thread Id =  ${Thread.currentThread.getId} / result = ${result}")

val future01: Future[String] = Future {
  result += 1
  s"[future01] Thread Id = ${Thread.currentThread.getId} / result = ${result}"
}

val future02: Future[String] = Future {
  result += 20
  s"[future02] Thread Id = ${Thread.currentThread.getId} / result = ${result}"
}

実行してみる。

checkComplete(future01)
checkComplete(future02)

// 実行結果
// [Main] Thread Id =  1 / result = 0
// Success([future01] Thread Id = 11 / result = 1)
// Success([future02] Thread Id = 11 / result = 21)

ThreadのIDが一緒なのは、future01の処理結果が帰るのが早すぎるのかな...。

これだとよく分からないので、下記の様にfuture01の処理を意図的に3秒停止させてみる。

val future01: Future[String] = Future {
  Thread.sleep(3000)
  result += 1
  s"[future01] Thread Id = ${Thread.currentThread.getId} / result = ${result}"
}

// 実行結果
// [Main] Thread Id =  1 / result = 0
// Success([future01] Thread Id = 11 / result = 21)
// Success([future02] Thread Id = 13 / result = 20)

future01の処理が3秒間止まっている間に、future02の処理が先に実行されたので、結果は

  • future02の返却値が20
  • future01の返却値が21

となった。

そしてThreadのIDもfuture01が11、future02が13となっている。

あと、100件のFutureを処理するこんなのも作ってみた。

(1 to 100).map { index =>
  Future {
    result += index
    s"Date ${new java.util.Date} - Thread id ${Thread.currentThread.getId} / index = ${index} / result = ${result}"
  }
} foreach { future => checkComplete(future) }

// 実行結果
// Success(Date Tue Mar 17 20:51:48 JST 2015 - Thread id 13 / index = 1 / result = 3)
// Success(Date Tue Mar 17 20:51:48 JST 2015 - Thread id 15 / index = 2 / result = 3)
// Success(Date Tue Mar 17 20:51:48 JST 2015 - Thread id 15 / index = 3 / result = 6)
// Success(Date Tue Mar 17 20:51:48 JST 2015 - Thread id 13 / index = 4 / result = 10)
// Success(Date Tue Mar 17 20:51:48 JST 2015 - Thread id 15 / index = 5 / result = 15)
// Success(Date Tue Mar 17 20:51:48 JST 2015 - Thread id 15 / index = 6 / result = 21)
// Success(Date Tue Mar 17 20:51:48 JST 2015 - Thread id 15 / index = 7 / result = 28)
// Success(Date Tue Mar 17 20:51:48 JST 2015 - Thread id 15 / index = 8 / result = 36)
// Success(Date Tue Mar 17 20:51:48 JST 2015 - Thread id 13 / index = 9 / result = 45)
// Success(Date Tue Mar 17 20:51:48 JST 2015 - Thread id 15 / index = 10 / result = 55)
// Success(Date Tue Mar 17 20:51:48 JST 2015 - Thread id 15 / index = 11 / result = 66)
// Success(Date Tue Mar 17 20:51:48 JST 2015 - Thread id 15 / index = 12 / result = 78)
// Success(Date Tue Mar 17 20:51:48 JST 2015 - Thread id 13 / index = 13 / result = 91)
// Success(Date Tue Mar 17 20:51:48 JST 2015 - Thread id 15 / index = 14 / result = 105)
// Success(Date Tue Mar 17 20:51:48 JST 2015 - Thread id 15 / index = 15 / result = 120)
// Success(Date Tue Mar 17 20:51:48 JST 2015 - Thread id 15 / index = 16 / result = 136)
// Success(Date Tue Mar 17 20:51:48 JST 2015 - Thread id 15 / index = 17 / result = 153)
// Success(Date Tue Mar 17 20:51:48 JST 2015 - Thread id 13 / index = 18 / result = 171)
// Success(Date Tue Mar 17 20:51:48 JST 2015 - Thread id 15 / index = 19 / result = 190)
// Success(Date Tue Mar 17 20:51:48 JST 2015 - Thread id 15 / index = 20 / result = 210)
// Success(Date Tue Mar 17 20:51:48 JST 2015 - Thread id 15 / index = 21 / result = 231)
// Success(Date Tue Mar 17 20:51:48 JST 2015 - Thread id 13 / index = 22 / result = 253)
// Success(Date Tue Mar 17 20:51:48 JST 2015 - Thread id 15 / index = 23 / result = 276)
// Success(Date Tue Mar 17 20:51:48 JST 2015 - Thread id 15 / index = 24 / result = 300)
// Success(Date Tue Mar 17 20:51:48 JST 2015 - Thread id 15 / index = 25 / result = 325)
// Success(Date Tue Mar 17 20:51:48 JST 2015 - Thread id 13 / index = 26 / result = 378)
// Success(Date Tue Mar 17 20:51:48 JST 2015 - Thread id 15 / index = 27 / result = 378)
// Success(Date Tue Mar 17 20:51:48 JST 2015 - Thread id 15 / index = 28 / result = 406)
// Success(Date Tue Mar 17 20:51:48 JST 2015 - Thread id 15 / index = 29 / result = 435)
// Success(Date Tue Mar 17 20:51:48 JST 2015 - Thread id 15 / index = 30 / result = 465)
// Success(Date Tue Mar 17 20:51:48 JST 2015 - Thread id 13 / index = 31 / result = 528)
// Success(Date Tue Mar 17 20:51:48 JST 2015 - Thread id 15 / index = 32 / result = 528)
// Success(Date Tue Mar 17 20:51:48 JST 2015 - Thread id 15 / index = 33 / result = 561)
// Success(Date Tue Mar 17 20:51:48 JST 2015 - Thread id 13 / index = 34 / result = 595)
// Success(Date Tue Mar 17 20:51:48 JST 2015 - Thread id 15 / index = 35 / result = 630)
// Success(Date Tue Mar 17 20:51:48 JST 2015 - Thread id 13 / index = 36 / result = 666)
// Success(Date Tue Mar 17 20:51:48 JST 2015 - Thread id 15 / index = 37 / result = 703)
// Success(Date Tue Mar 17 20:51:48 JST 2015 - Thread id 13 / index = 38 / result = 741)
// Success(Date Tue Mar 17 20:51:48 JST 2015 - Thread id 15 / index = 39 / result = 780)
// Success(Date Tue Mar 17 20:51:48 JST 2015 - Thread id 13 / index = 40 / result = 820)
// Success(Date Tue Mar 17 20:51:48 JST 2015 - Thread id 15 / index = 41 / result = 861)
// Success(Date Tue Mar 17 20:51:48 JST 2015 - Thread id 13 / index = 42 / result = 903)
// Success(Date Tue Mar 17 20:51:48 JST 2015 - Thread id 15 / index = 43 / result = 946)
// Success(Date Tue Mar 17 20:51:48 JST 2015 - Thread id 13 / index = 44 / result = 990)
// Success(Date Tue Mar 17 20:51:48 JST 2015 - Thread id 15 / index = 45 / result = 1035)
// Success(Date Tue Mar 17 20:51:48 JST 2015 - Thread id 13 / index = 46 / result = 1081)
// Success(Date Tue Mar 17 20:51:48 JST 2015 - Thread id 15 / index = 47 / result = 1128)
// Success(Date Tue Mar 17 20:51:48 JST 2015 - Thread id 13 / index = 48 / result = 1176)
// Success(Date Tue Mar 17 20:51:48 JST 2015 - Thread id 15 / index = 49 / result = 1225)
// Success(Date Tue Mar 17 20:51:48 JST 2015 - Thread id 13 / index = 50 / result = 1275)
// Success(Date Tue Mar 17 20:51:48 JST 2015 - Thread id 15 / index = 51 / result = 1326)
// Success(Date Tue Mar 17 20:51:48 JST 2015 - Thread id 13 / index = 52 / result = 1378)
// Success(Date Tue Mar 17 20:51:48 JST 2015 - Thread id 15 / index = 53 / result = 1431)
// Success(Date Tue Mar 17 20:51:48 JST 2015 - Thread id 13 / index = 54 / result = 1485)
// Success(Date Tue Mar 17 20:51:48 JST 2015 - Thread id 15 / index = 55 / result = 1540)
// Success(Date Tue Mar 17 20:51:48 JST 2015 - Thread id 13 / index = 56 / result = 1596)
// Success(Date Tue Mar 17 20:51:48 JST 2015 - Thread id 15 / index = 57 / result = 1653)
// Success(Date Tue Mar 17 20:51:48 JST 2015 - Thread id 13 / index = 58 / result = 1711)
// Success(Date Tue Mar 17 20:51:48 JST 2015 - Thread id 15 / index = 59 / result = 1770)
// Success(Date Tue Mar 17 20:51:48 JST 2015 - Thread id 13 / index = 60 / result = 1830)
// Success(Date Tue Mar 17 20:51:48 JST 2015 - Thread id 15 / index = 61 / result = 1891)
// Success(Date Tue Mar 17 20:51:48 JST 2015 - Thread id 13 / index = 62 / result = 1953)
// Success(Date Tue Mar 17 20:51:48 JST 2015 - Thread id 15 / index = 63 / result = 2016)
// Success(Date Tue Mar 17 20:51:48 JST 2015 - Thread id 13 / index = 64 / result = 2080)
// Success(Date Tue Mar 17 20:51:48 JST 2015 - Thread id 15 / index = 65 / result = 2145)
// Success(Date Tue Mar 17 20:51:48 JST 2015 - Thread id 13 / index = 66 / result = 2211)
// Success(Date Tue Mar 17 20:51:48 JST 2015 - Thread id 15 / index = 67 / result = 2278)
// Success(Date Tue Mar 17 20:51:48 JST 2015 - Thread id 13 / index = 68 / result = 2346)
// Success(Date Tue Mar 17 20:51:48 JST 2015 - Thread id 15 / index = 69 / result = 2415)
// Success(Date Tue Mar 17 20:51:48 JST 2015 - Thread id 13 / index = 70 / result = 2485)
// Success(Date Tue Mar 17 20:51:48 JST 2015 - Thread id 15 / index = 71 / result = 2556)
// Success(Date Tue Mar 17 20:51:48 JST 2015 - Thread id 13 / index = 72 / result = 2628)
// Success(Date Tue Mar 17 20:51:48 JST 2015 - Thread id 15 / index = 73 / result = 2701)
// Success(Date Tue Mar 17 20:51:48 JST 2015 - Thread id 13 / index = 74 / result = 2775)
// Success(Date Tue Mar 17 20:51:48 JST 2015 - Thread id 15 / index = 75 / result = 2850)
// Success(Date Tue Mar 17 20:51:48 JST 2015 - Thread id 13 / index = 76 / result = 2926)
// Success(Date Tue Mar 17 20:51:48 JST 2015 - Thread id 15 / index = 77 / result = 3003)
// Success(Date Tue Mar 17 20:51:48 JST 2015 - Thread id 13 / index = 78 / result = 3081)
// Success(Date Tue Mar 17 20:51:48 JST 2015 - Thread id 15 / index = 79 / result = 3160)
// Success(Date Tue Mar 17 20:51:48 JST 2015 - Thread id 13 / index = 80 / result = 3240)
// Success(Date Tue Mar 17 20:51:48 JST 2015 - Thread id 15 / index = 81 / result = 3321)
// Success(Date Tue Mar 17 20:51:48 JST 2015 - Thread id 13 / index = 82 / result = 3403)
// Success(Date Tue Mar 17 20:51:48 JST 2015 - Thread id 15 / index = 83 / result = 3486)
// Success(Date Tue Mar 17 20:51:48 JST 2015 - Thread id 13 / index = 84 / result = 3570)
// Success(Date Tue Mar 17 20:51:48 JST 2015 - Thread id 15 / index = 85 / result = 3655)
// Success(Date Tue Mar 17 20:51:48 JST 2015 - Thread id 13 / index = 86 / result = 3741)
// Success(Date Tue Mar 17 20:51:48 JST 2015 - Thread id 15 / index = 87 / result = 3828)
// Success(Date Tue Mar 17 20:51:48 JST 2015 - Thread id 13 / index = 88 / result = 3916)
// Success(Date Tue Mar 17 20:51:48 JST 2015 - Thread id 15 / index = 89 / result = 4005)
// Success(Date Tue Mar 17 20:51:48 JST 2015 - Thread id 13 / index = 90 / result = 4095)
// Success(Date Tue Mar 17 20:51:48 JST 2015 - Thread id 15 / index = 91 / result = 4186)
// Success(Date Tue Mar 17 20:51:48 JST 2015 - Thread id 13 / index = 92 / result = 4278)
// Success(Date Tue Mar 17 20:51:48 JST 2015 - Thread id 15 / index = 93 / result = 4371)
// Success(Date Tue Mar 17 20:51:48 JST 2015 - Thread id 13 / index = 94 / result = 4465)
// Success(Date Tue Mar 17 20:51:48 JST 2015 - Thread id 15 / index = 95 / result = 4560)
// Success(Date Tue Mar 17 20:51:48 JST 2015 - Thread id 13 / index = 96 / result = 4656)
// Success(Date Tue Mar 17 20:51:48 JST 2015 - Thread id 15 / index = 97 / result = 4753)
// Success(Date Tue Mar 17 20:51:48 JST 2015 - Thread id 13 / index = 98 / result = 4851)
// Success(Date Tue Mar 17 20:51:48 JST 2015 - Thread id 15 / index = 99 / result = 4950)
// Success(Date Tue Mar 17 20:51:48 JST 2015 - Thread id 13 / index = 100 / result = 5050)

ちなみに、ThreadIDが2つなのは、僕のMacのCPUがIntel Core2 Duoだから(だと思っている)。

いっちゃんええCPUで試してみたいなーwww

さいごに

つっこみあったらおねがいします。