web-dev-qa-db-fra.com

Coroutines: runBlocking vs coroutineScope

Je lisais Bases de Coroutine essayant de comprendre et d'apprendre.

Il y a une partie avec ce code:

fun main() = runBlocking { // this: CoroutineScope
    launch { 
        delay(200L)
        println("Task from runBlocking")
    }

    coroutineScope { // Creates a new coroutine scope
        launch {
            delay(900L) 
            println("Task from nested launch")
        }

        delay(100L)
        println("Task from coroutine scope") // This line will be printed before nested launch
    }

    println("Coroutine scope is over") // This line is not printed until nested launch completes
}

La sortie se passe comme suit:

Task from coroutine scope
Task from runBlocking
Task from nested launch
Coroutine scope is over

Ma question est pourquoi cette ligne:

 println("Coroutine scope is over") // This line is not printed until nested launch completes

s'appelle toujours dernier?

Ne devrait-il pas être appelé depuis le:

coroutineScope { // Creates a new coroutine scope
    ....
}

est suspendue?

Il y a aussi une note ici:

La principale différence entre runBlocking et coroutineScope est que ce dernier ne bloque pas le thread en cours en attendant que tous les enfants soient terminés.

Je ne comprends pas en quoi coroutineScope et runBlocking sont différents ici? coroutineScope ressemble à son blocage car il n’atteint que la dernière ligne à la fin.

Quelqu'un peut-il m'éclairer ici?

Merci d'avance.

24

Je ne comprends pas en quoi coroutineScope et runBlocking sont différents ici? coroutineScope ressemble à son blocage car il n’atteint que la dernière ligne à la fin.

Du point de vue du code dans le bloc, votre compréhension est correcte. La différence entre runBlocking et coroutineScope se produit à un niveau inférieur: qu'arrive-t-il au thread lorsque la coroutine est bloquée?

  • runBlocking n'est pas un suspend fun. Le fil qui l'a appelé reste à l'intérieur jusqu'à la fin de la coroutine.

  • coroutineScope est un suspend fun. Si votre coroutine est suspendue, la fonction coroutineScope est également suspendue. Cela permet à la fonction de niveau supérieur, une fonction sans suspension qui a créé le coroutine, de continuer à s'exécuter sur le même thread. Le thread a "échappé" au bloc coroutineScope et est prêt à effectuer d'autres tâches.

Dans votre exemple spécifique: lorsque coroutineScope est suspendu, le contrôle retourne au code d'implémentation à l'intérieur de runBlocking. Ce code est une boucle d'événement qui gère toutes les lignes que vous avez commencées. Dans votre cas, il y aura des routines programmées pour s'exécuter après un délai. Lorsque le moment sera venu, il reprendra la coroutine appropriée, qui durera quelques instants, sera suspendue puis le contrôle sera à nouveau à l'intérieur de runBlocking.


Bien que ce qui précède décrit les similitudes conceptuelles, il devrait également vous montrer que runBlocking est un outil complètement différent de coroutineScope.

  • runBlocking est une construction de bas niveau, à utiliser uniquement dans du code d'infrastructure ou dans des exemples autonomes comme le vôtre. Il transforme un thread existant en boucle d'événement et crée sa coroutine avec un Dispatcher qui publie des coroutines de reprise dans la file d'attente de la boucle d'événement.

  • coroutineScope est une construction orientée utilisateur, utilisée pour délimiter les limites d'une tâche en cours de décomposition parallèle. Vous l'utilisez pour attendre commodément tout le travail async qui s'y déroule, obtenir le résultat final et gérer toutes les pannes en un point central.

23
Marko Topolnik

runBlocking just bloque le thread actuel jusqu'à ce que les coroutines internes soient terminés. Ici, le thread qui exécute runBlocking sera bloqué jusqu'a la coroutine de coroutineScope sera terminée.

Tout d'abord, launch n'autorisera pas le thread à exécuter les instructions venant après runBlocking, mais autorisera les instructions immédiatement après ce bloc launch - c'est pourquoi Task from coroutine scope est imprimé avant que Task from runBlocking.

Mais imbriqué coroutineScope dans le contexte de runBlocking n'autorisera pas le thread à exécuter les instructions qui viendront après ce bloc coroutineScope, car runBlocking bloquera le thread. jusqu'à ce que la coroutine de coroutineScope soit complètement terminée. Et c'est pourquoi Coroutine scope is over viendra toujours après Task from nested launch.

5
Andrey Ilyunin

De cet article merveilleux https://jivimberg.io/blog/2018/05/04/parallel-map-in-kotlin/

suspend fun <A, B> Iterable<A>.pmap(f: suspend (A) -> B): List<B> = coroutineScope {
    map { async { f(it) } }.awaitAll()
}

Avec runBlocking, nous n’utilisions pas la concurrence simultanée structurée. Par conséquent, l’invocation de f pouvait échouer et toutes les autres exécutions se poursuivaient sans faille. Et aussi nous ne jouions pas à Nice avec le reste du code. En utilisant runBlocking, nous avons bloqué le thread de manière forcée jusqu'à la fin de l'exécution de pmap, au lieu de laisser l'appelant décider du déroulement de l'exécution.

0
onmyway133