32 votes

Kotlin Coroutines Async Await Sequence

Pouvez-vous m'expliquer quelle est la différence entre ces deux blocs de code ? La première fois, le résultat est de 421, mais la seconde fois, il est de 606. Pourquoi le premier est parallèle et le second séquentiel ?

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
}

58voto

Marko Topolnik Points 77257
val one = async { one() }
val two = async { two() }
val int1 = one.await()
val int2 = two.await()

Ce qu'il fait :

  1. première tâche de reproduction
  2. deuxième tâche de reproduction
  3. attendre sur la première tâche
  4. attendez la deuxième tâche

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

Ce qu'il fait :

  1. première tâche de reproduction
  2. attendre sur la première tâche
  3. deuxième tâche de reproduction
  4. attendez la deuxième tâche

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

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

8voto

Roland Points 8368

Dans la première variante, vous obtenez un Deferred<Int> pour les deux appels asynchrones. En tant que la documentation de Deferred montre bien qu'il existe plusieurs états dans lesquels l'objet différé peut se trouver. Vu de l'extérieur, cet état est soit new o active mais sûrement pas encore achevée. Dans votre deuxième variante, cependant, le premier async-await a besoin d'un completed Sinon, vous n'auriez aucune valeur dans ce domaine. Cependant, sur votre async{one()}.await() le deuxième async n'est pas encore connue. Notez également que la valeur de retour de await() est maintenant Int et non Deferred la coroutine doit donc avoir été exécutée à ce moment-là. Vérifier également la documentation de await() .

En d'autres termes :

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

Les deux one y two sont maintenant Deferred<Int> . Aucun n'a encore été appelé (ou pourrait l'avoir été). Dès que vous appelez one.await() Il se peut qu'il ait déjà commencé les deux one y two simplement parce qu'il dispose des ressources nécessaires (même si vous n'avez pas utilisé la fonction two.await() n'importe où dans votre code).

En revanche, pour la deuxième variante :

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

Même s'il crée une coroutine pour async {one()} il doit attribuer une valeur à one immédiatement, car vous appelez await() sur celui-ci. Les types de one y 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. Personne ne sait alors qu'un autre appel asynchrone doit être exécuté pendant que nous attendons la valeur de la première ligne. Si la première n'avait pas de await la coroutine sera à nouveau exécutée en parallèle, par exemple :

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

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

On peut donc peut-être résumer cela en disant que seules les coroutines qui sont connues/spawnées à ce moment-là peuvent être exécutées en parallèle lors d'un await.

7voto

MohamedHarmoush Points 65

Les règles du pouce :

  • Utilisation avecContexte lorsque vous n'avez pas besoin de l'exécution parallèle.
  • Utilisation asynchrone uniquement lorsque vous avez besoin de l'exécution parallèle. Les deux avecContexte et asynchrone peut être utilisé pour obtenir un résultat qui n'est pas possible avec le lancement.
  • Utilisation avecContexte pour renvoyer le résultat d'une seule tâche".
  • Utilisez async pour les résultats de plusieurs tâches exécutées en parallèle.

Vérifier cette pour plus de détails

0voto

toffor Points 150

Après la réponse de Marko Topolnik, j'ai essayé différentes variantes et je pense que la réponse est acceptée. Mais une chose intéressante est que si je démarre une coroutine et que je n'appelle pas await, 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 résultats sont,

func1 start, iteration 1
func2 start, iteration 1
func1 end, itération 1
func2 end, itération 1
temps : 430
func1 start, iteration 2
func1 end, itération 2
func2 start, iteration 2
func2 end, itération 2
heure : 607
func1 début, itération 3
durée : 2
func2 start, iteration 3

Le processus s'est terminé avec le code de sortie 0

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X