39
34

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

Futureにハマる

Last updated at Posted at 2015-02-18

以下の何の変哲もないコード。
メインスレッドは早々に終了しないようにAwaitでFutureの終了を待っている。1s後に「The result is 4」と表示される。
うん、期待通り。めでたしめでたし。

import scala.concurrent._
import ExecutionContext.Implicits.global
import scala.concurrent.duration.Duration
import scala.util.{ Success, Failure }

object Main extends App {
  val n = 2

  val f: Future[Int] = Future {
    Thread.sleep(1000)
    n * 2
  }
  f.onComplete {
    case Success(r) => println("The result is " + r)
    case Failure(t) => println(t.getMessage)
  }

  Await.ready(f, Duration.Inf)
}

ではこれだとどうか?
onComplete部分での表示に溜めを作ってみる。重い処理と読み替えてもいい。

  f.onComplete {
    case Success(r) =>
      println("The result is ...")
      Thread.sleep(1000)
      println(r)
    case Failure(t) => println(t.getMessage)
  }

1s後に「The result is ...」が表示されて、更に1s待って「4」と表示され...ない。「The result is ...」は出るけど。
というのが今回のハマりどころ。

ドキュメントをよく読んでみるとonCompleteはimplicitパラメータとしてExecutionContextを必要とするのでした。
つまりメインとは別スレッドとして実行される。
確認のためスレッドidを出力するようにしてみる。

object Main extends App {
  println("main: " + Thread.currentThread.getId) // ここ

  val n = 2

  val f: Future[Int] = Future {
    println("future: " + Thread.currentThread.getId) // ここ
    Thread.sleep(1000)
    n * 2
  }
  f.onComplete {
    case Success(r) =>
      println("onSuccess: " + Thread.currentThread.getId) // ここ
      println("The result is ...")
      Thread.sleep(1000)
      println(r)
    case Failure(t) => println(t.getMessage)
  }
   
  Await.ready(f, Duration.Inf)
}

結果はこう。

main: 28
future: 29
onSuccess: 29

やはり。
つまり、AwaitでFutureの終了自体は待つけども、success時の(failure時の)処理は別スレッドで処理されるので結局非同期に動き処理途中でもメインスレッドは終了してしまう。
冒頭のコードはロスタイムっぽい時間でたまたま動いていたということか。
というわけで、プログラムを変更。

object Main extends App {
  val n = 2

  val f: Future[Int] = Future {
    Thread.sleep(1000)
    n * 2
  }

  Await.ready(f, Duration.Inf)

  println("The result is ...")
  Thread.sleep(1000)
  f.value.get match {
    case Success(r) => println(r)
    case Failure(t) => println(t.getMessage)
  }
}

これでFutureの終わりをAwaitで待ってから、その後の処理をメインスレッドで実行することになるので重い処理か軽い処理かとか気にすることなく期待通りの結果が得られるようになった。

39
34
2

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
39
34

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?