web-dev-qa-db-fra.com

Kotlin Coroutines Async Await Sequence

Pouvez-vous s'il vous plaît m'expliquer quelle est la différence entre ces deux blocs de code. La première fois imprime 421, mais la seconde imprime 606. Pourquoi la première est parallèle et la seconde séquentielle?

fun main(args: Array<String>) = runBlocking {

    var time = measureTimeMillis {
        val one = async { one() }
        val two = async { two() }
        val int1 = one.await()
        val int2 = two.await()
        println(int1 + int2)

    }

    println(time)


    time = measureTimeMillis {
        val one = async { one() }.await()
        val two = async { two() }.await()
        println(one + two)

    }

    print(time)
}

suspend fun one(): Int {
    delay(200)
    return 12
}

suspend fun two(): Int {
    delay(400)
    return 23
}
7
toffor
val one = async { one() }
val two = async { two() }
val int1 = one.await()
val int2 = two.await()

Ce que cela fait:

  1. engendrer une tâche
  2. engendrer la tâche deux
  3. attendre sur la première tâche
  4. attendre la deuxième tâche

val one = async { one() }.await()
val two = async { two() }.await()

Ce que cela fait:

  1. engendrer une tâche
  2. attendre sur la première tâche
  3. engendrer la tâche deux
  4. attendre la deuxième tâche

Il n'y a pas de concurrence ici, c'est du code purement séquentiel. En fait, pour une exécution séquentielle, vous ne devriez même pas utiliser async. L'idiome approprié est

val one = withContext(Dispatchers.Default) { one() }
val two = withContext(Dispatchers.Default) { two() }
15
Marko Topolnik

Dans la première variante, vous obtenez un Deferred<Int> Pour les deux appels asynchrones. Comme la documentation de Deferred montre si bien qu'il y a plusieurs états dans lesquels l'objet différé peut être. De l'extérieur, cet état est maintenant soit new ou active mais sûrement pas encore terminé. Cependant, sur votre deuxième variante, le premier async-wait a déjà besoin d'un état completed sinon vous ne pourriez pas y avoir de valeur. Cependant sur votre async{one()}.await() la seconde async n'est pas encore connue. Notez également que la valeur de retour de await() est désormais Int et non Deferred, donc la coroutine doit avoir été exécutée d'ici là. Vérifiez également la documentation de await() .

En d'autres termes:

val one = async { one() }
val two = async { two() }

one et two sont maintenant Deferred<Int>. Aucun n'a encore été appelé (ou aurait pu l'être encore). Dès que vous appelez one.await(), il peut déjà démarrer à la fois one et two, juste parce qu'il a les ressources nécessaires (même si vous n'avez pas utilisé la fonction two.await() n'importe où dans votre code).

Sur la deuxième variante cependant:

val one = async { one() }.await()
val two = async { two() }.await()

Même s'il crée une coroutine pour async {one()}, il doit définir immédiatement une valeur one, car vous appelez await() dessus. Les types de one et two sont tous deux Int. Ainsi, dès que la première de ces lignes est atteinte, le code asynchrone doit être exécuté immédiatement. D'ici là, personne ne sait qu'un autre appel asynchrone doit être exécuté pendant que nous attendons la valeur du premier. Si le premier n'avait pas de await, la coroutine serait à nouveau exécutée en parallèle, par exemple:

val one = async { one() }
val two = async { two() }.await()

exécutera one() et two() en parallèle.

Alors peut-être que cela peut se résumer à: seules ces coroutines peuvent être exécutées en parallèle en attente, qui sont connues/générées d'ici là.

1
Roland

Après la réponse de Marko Topolnik, j'ai essayé différentes variantes et je pense que sa réponse a été acceptée. Mais une chose intéressante est que si je démarre coroutine et n'appelle pas wait, la fonction démarre mais ne se termine pas. Voici mon code.

fun main(args: Array<String>) = runBlocking {

    var time = measureTimeMillis {
        val one = async { one(1) }
        val two = async { two(1) }
        val int1 = one.await()
        val int2 = two.await()
    }

    println("time: $time")


    time = measureTimeMillis {
        val one = async { one(2) }.await()
        val two = async { two(2) }.await()
    }

    println("time: $time")

    time = measureTimeMillis {
        val one = async { one(3) }
        val two = async { two(3) }
    }

    println("time: $time")



}

suspend fun one(iteration: Int): Int {
    println("func1 start, iteration $iteration")
    delay(200)
    println("func1 end, iteration $iteration")
    return 12
}

suspend fun two(iteration: Int): Int {
    println("func2 start, iteration $iteration")
    delay(400)
    println("func2 end, iteration $iteration")
    return 23
}

Et les sorties sont,

func1 start, itération 1
func2 start, itération 1
func1 end, itération 1
fin func2, itération 1
temps: 430
func1 start, itération 2
func1 end, itération 2
func2 start, itération 2
func2 end, itération 2
temps: 607
func1 start, itération 3
temps: 2
func2 start, itération 3

Processus terminé avec le code de sortie 0

0
toffor