まーぽんって誰がつけたの?

iOS→Scala→インフラなおじさん技術メモ

Future.sequenceは1つずつ順番に実行される訳じゃない

sequenceっていう名前からどうしても順序を意識してしまって順番に(直列に)実行されるものだと思っていた。 でもそうじゃなかった。

Future.applyの場合

ためしに、こんな処理を書いてみる。1から10までを1s待って出力するだけのやつ。

def process: Future[Seq[Int]] = Future.sequence {
  for {
    i <- 1 to 10
  } yield {
    Future {
      println(s"start -> ${i.toString} ${System.currentTimeMillis}")
      Thread.sleep(1000)
      println(s"end -> ${i.toString} ${System.currentTimeMillis}")
      i
    }
  }
}

これを実行すると、出力はこうなる。同時に4つの処理が走るみたいだ。並列で同時に実行されているので全ての処理が終わるまでに3秒ぐらいで終わっている。ただ、最終的な結果は、Vector(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)となって順番通りになっている。

scala> Await.result(process, Duration.Inf)
start -> 1 1459695509918
start -> 3 1459695509919
start -> 2 1459695509919
start -> 4 1459695509919
end -> 3 1459695510923
end -> 4 1459695510923
end -> 1 1459695510923
start -> 6 1459695510923
start -> 7 1459695510923
end -> 2 1459695510923
start -> 5 1459695510923
start -> 8 1459695510923
end -> 8 1459695511929
end -> 7 1459695511929
start -> 9 1459695511929
start -> 10 1459695511929
end -> 5 1459695511929
end -> 6 1459695511929
end -> 9 1459695512933
end -> 10 1459695512933
res24: Seq[Int] = Vector(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)

Future.successfulの場合

こんな風にFuture.successfulで書き直して

def process: Future[Seq[Int]] = Future.sequence {
  for {
    i <- 1 to 10
  } yield {
    Future.successful {
      println(s"start -> ${i.toString} ${System.currentTimeMillis}")
      Thread.sleep(1000)
      println(s"end -> ${i.toString} ${System.currentTimeMillis}")
      i
    }
  }
}

実行すると、順番に実行される。実行時間も10秒ぐらいかかってる。結果もVector(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)で同じになってる。

scala> Await.result(process, Duration.Inf)
start -> 1 1459696443179
end -> 1 1459696444180
start -> 2 1459696444180
end -> 2 1459696445180
start -> 3 1459696445180
end -> 3 1459696446180
start -> 4 1459696446181
end -> 4 1459696447181
start -> 5 1459696447182
end -> 5 1459696448186
start -> 6 1459696448186
end -> 6 1459696449187
start -> 7 1459696449187
end -> 7 1459696450188
start -> 8 1459696450188
end -> 8 1459696451188
start -> 9 1459696451188
end -> 9 1459696452188
start -> 10 1459696452188
end -> 10 1459696453191
res25: Seq[Int] = Vector(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)

Future.successfulで同じことをした場合は、順番に行われる。これは、Future.applyFuture.successfulの処理をとるときに、名前渡しかそうじゃないかが関係ある。(となんとなく思う)

scala/Future.scala at 2.11.x · scala/scala · GitHub

def apply[T](body: =>T)(implicit @deprecatedName('execctx) executor: ExecutionContext): Future[T] = impl.Future(body)
def successful[T](result: T): Future[T] = Promise.successful(result).future