Help us understand the problem. What is going on with this article?

Futureにハマる

More than 5 years have passed since last update.

以下の何の変哲もないコード。
メインスレッドは早々に終了しないように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で待ってから、その後の処理をメインスレッドで実行することになるので重い処理か軽い処理かとか気にすることなく期待通りの結果が得られるようになった。

mtoyoshi
調査モノのアウトプットが中心です
fringe81
Fringeは、最新のテクノロジーとプロフェッショナルによるサービスにより、社会課題に仮説を立てて市場に広げていくことで、数十年という長期的なスパンで価値を生み出し続け、より良い世界を創る集団です。 既存の領域に限らず、時流を読み、仮説を生み出し、テクノロジーの力で優れたサービスを生み出し続けます。
https://www.fringe81.com/
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away