web-dev-qa-db-fra.com

Pourquoi Future.Squence exécute-t-il mes futurs en parallèle plutôt qu'en série?

Le mot "séquence" signifie une série d'actions l'une après l'autre. 

object Test {

  def main(args: Array[String]) {

    def producer() = {
      val list = Seq(
          future { println("startFirst"); Thread.sleep(3000); println("stopFirst") }, 
          future { println("startSecond"); Thread.sleep(1000); println("stopSecond") }
      )
      Future.sequence(list)
    }

   Await.result(producer, Duration.Inf)
  }
}

Par conséquent, je prévois que ce programme imprimera: startFirst stopFirst startSecond stopSecond

ou même: startSecond stopSecond startFirst stopFirst

mais pas (comme cela arrive): startFirst startSecond stopSecond stopFirst

Pourquoi cette méthode ne s'appelle pas Future.parallel()? Et que devrais-je utiliser pour garantir que tous les futurs dans une Seq de futurs sont déclenchés en série (et non en parallèle)?

29
sscarduzio

Les futures s'exécutent simultanément car elles ont été démarrées simultanément:) . Pour les exécuter séquentiellement, vous devez utiliser flatMap:

Future { println("startFirst"); 
         Thread.sleep(3000); 
         println("stopFirst") 
        }.flatMap{
         _ =>  Future { 
                       println("startSecond"); 
                       Thread.sleep(1000); 
                       println("stopSecond") 
               }
        }

Future.sequence tourne simplement Seq[Future[T]] => Future[Seq[T]], ce qui signifie rassembler les résultats de tous les contrats à terme déjà commencés et les mettre à l’avenir.

34
grotrianster

un petit changement à la future Future.sequence rendra la future exécution sérialisée:

def seq[A, M[X] <: TraversableOnce[X]](in: M[() => Future[A]])(implicit cbf: CanBuildFrom[M[()=>Future[A]], A, M[A]], executor: ExecutionContext): Future[M[A]] = {
    in.foldLeft(Future.successful(cbf(in))) {
       (fr, ffa) => for (r <- fr; a <- ffa()) yield (r += a)
    } map (_.result())
}

et votre code ressemblera à ceci:

object Test {
def main(args: Array[String]) {

    def producer() = {
      val list = Seq(
          {() => future { println("startFirst"); Thread.sleep(3000); println("stopFirst") }}, 
          {() => future { println("startSecond"); Thread.sleep(1000); println("stopSecond") }}
      )
      FutureExt.seq(list)
    }

    Await.result(producer, Duration.Inf)
    }
}

qui ressemble beaucoup à votre code d'origine et avec la même collection de résultats avec le fichier original Future.sequence ()

10
eagle yuan

Linéariser:

import scala.concurrent._
import scala.collection.mutable.Builder
import scala.collection.generic.CanBuildFrom
import language.higherKinds

/**
 * Linearize asynchronously applies a given function in-order to a sequence of values, producing a Future with the result of the function applications.
 * Execution of subsequent entries will be aborted if an exception is thrown in the application of the function.
 */
def linearize[T, U, C[T] <: Traversable[T]](s: C[T])(f: T => U)(implicit cbf: CanBuildFrom[C[T], U, C[U]], e: ExecutionContext): Future[C[U]] = {
  def next(i: Iterator[T], b: Builder[U, C[U]]): Future[C[U]] = if(!i.hasNext) Future successful b.result else Future { b += f(i.next()) } flatMap { b => next(i, b) }
  next(s.toIterator, cbf(s))
}

scala> linearize(1 to 100)(_.toString) foreach println

scala> Vector(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100)

De: https://Gist.github.com/viktorklang/3347939

4
Viktor Klang

Si vous avez une séquence de certaines valeurs et que vous souhaitez les mapper à Future et les exécuter en série:

import scala.concurrent.ExecutionContext.Implicits.global

implicit class SeqExtension[A](s: Seq[A]) {
  def foldLeftToFuture[B](initial: B)(f: (B, A) => Future[B])(implicit ec: ExecutionContext): Future[B] =
    s.foldLeft(Future(initial))((future, item) => future.flatMap(f(_, item)))

  def mapInSeries[B](f: A => Future[B])(implicit ec: ExecutionContext): Future[Seq[B]] =
    s.foldLeftToFuture(Seq[B]())((seq, item) => f(item).map(seq :+ _))
}

val stringsFuture: Future[Seq[String]] = Seq(1, 2, 3).mapInSeries[String](i => Future(i.toString))
val strings = Await.result(stringsFuture, Duration.Inf) // List("1", "2", "3")
2
mixel

ou vous pouvez utiliser pour le bloc de rendement {..} pour obtenir la séquence de Future. Future.sequence convertir Seq [Future] en Future [Seq]

0
Gomes