J'essaie de construire une nouvelle structure de contrôle qui crée un fil pour chacun de ses arguments et les exécute en parallèle. Le code semble aller bien lorsque j'ai construit deux futurs manuellement pour chaque entrée, car je vois que le thread rapide se termine avant le thread lent.

Voici la sortie:

fast
slow

Cependant, si j'utilise List(a, b).map(f => Future {f}), je vois toujours que le thread rapide est exécuté une fois que le ralentissement est terminé. Voici la sortie:

slow
fast

Quelqu'un peut-il expliquer cela?

Code collé ici:

import java.util.concurrent.Executors

import scala.concurrent.{ExecutionContext, Future}

object ExecInParallel extends App {

  def run(a: => Unit, b: => Unit): Unit = {

    val executorService = Executors.newFixedThreadPool(2)
    implicit val executionContext = 
    ExecutionContext.fromExecutorService(executorService)

    // af and bf are executed in parallel
    val af = Future(a)
    val bf = Future(b)

    // however, the following code is not parallel
    List(a, b).map(f => Future(f))

    Thread.sleep(3000)
    executorService.shutdown
  }

  run(
    {
      Thread.sleep(2000)
      println("slow")
    }, 
    {
      Thread.sleep(1000)
      println("fast")
    }
  )
}
0
K.Chen 26 avril 2017 à 03:35

3 réponses

Meilleure réponse

C'est parce que a et b sont évalués à chaque fois qu'ils sont référencés dans une position sans nom et les arguments List(a, b) ne sont pas par nom. Depuis https://en.wikipedia.org/wiki/Evaluation_strategy#Call_by_name:

L'appel par nom est une stratégie d'évaluation dans laquelle les arguments d'une fonction ne sont pas évalués avant que la fonction ne soit appelée - plutôt, ... puis laissés pour être évalués chaque fois qu'ils apparaissent dans la fonction. Si un argument n'est pas utilisé dans le corps de la fonction, l'argument n'est jamais évalué; s'il est utilisé plusieurs fois, il est réévalué à chaque fois qu'il apparaît.

En fait, cela équivaut à ce code:

List({
  Thread.sleep(2000)
  println("slow")
  }, 
  {
    Thread.sleep(1000)
    println("fast")
  }).map(f => Future(f))

Puisque le constructeur List ne prend pas d'arguments par nom, ces valeurs sont évaluées avant que la liste elle-même ne soit construite

1
pedrofurla 26 avril 2017 à 01:41

Votre nom a et b est exécuté (séquentiellement) dans List(a, b), avant la construction de Future dans map. Si vous cochez le type inféré de List(a, b), vous verrez qu'il s'agit d'un List[Unit].

Pour réaliser ce que vous vouliez, vous avez besoin d'une liste de fonctions plutôt que d'une liste de résultats.

Les éléments suivants fonctionneront:

List(a _, b _).map(f => Future(f()))
1
PH88 26 avril 2017 à 01:44

Cela se produit car vous créez d'abord une liste de deux valeurs de nom d'appel:

List(a, b)...

Et jusqu'à ce que a et b ne soient pas entièrement calculés, l'opération de carte n'est pas exécutée. Lorsque List (a, b) est prêt, vous l'enveloppez dans Futures:

List(a, b).map(f => Future(f)) 
1
Nyavro 26 avril 2017 à 01:38