web-dev-qa-db-fra.com

kotlin coroutines, quelle est la différence entre coroutineScope et withContext

withContext
suspend fun <T> withContext(
    context: CoroutineContext, 
    block: suspend CoroutineScope.() -> T
): T (source)
Calls the specified suspending block with a given coroutine context, suspends until it completes, and returns the result.
suspend fun <R> coroutineScope(
    block: suspend CoroutineScope.() -> R
): R (source)
Creates a CoroutineScope and calls the specified suspend block with this scope. The provided scope inherits its coroutineContext from the outer scope, but overrides the context’s Job.

withContext prend CoroutineContext, et les deux semblent être complete une fois tous ses enfants terminés.

Dans quel cas le withContext ou le coroutineScope doit être préféré à l'autre?

par exemple:

suspend fun processAllPages() = withContext(Dispatchers.IO) { 
    // withContext waits for all children coroutines 
    launch { processPages(urls, collection) }
    launch { processPages(urls, collection2) }
    launch { processPages(urls, collection3) }
}

pourrait également être

suspend fun processAllPages() = coroutineScope { 
    // coroutineScope waits for all children coroutines 
    launch { processPages(urls, collection) }
    launch { processPages(urls, collection2) }
    launch { processPages(urls, collection3) }
}

les deux processAllPages() font-ils la même chose?


mise à jour: voir discuter sur Pourquoi withContext attend-il la fin des coroutines enfants

10
lannyf

Formellement, coroutineScope est un cas particulier de withContext où vous passez dans le contexte actuel, en évitant tout changement de contexte. Schématiquement,

coroutineScope ≡ withContext(this.coroutineContext)

Étant donné que le changement de contexte n'est qu'une des nombreuses fonctionnalités de withContext, il s'agit d'un cas d'utilisation légitime. withContext attend que toutes les coroutines que vous démarrez dans le bloc se terminent. Si l'un d'eux échoue, il annulera automatiquement toutes les autres coroutines et le bloc entier lèvera une exception, mais n'annulera pas automatiquement la coroutine à partir de laquelle vous l'appelez.

Chaque fois que vous avez besoin de ces fonctionnalités sans avoir à changer de contexte, vous devriez toujours préférer coroutineScope car cela signale votre intention beaucoup plus clairement.

coroutineScope concerne le cycle de vie de plusieurs sous-coroutines. Il est utilisé pour décomposer une tâche en plusieurs sous-tâches simultanées. Vous ne pouvez pas changer le contexte avec lui, donc il hérite du Dispatcher du contexte actuel. En général, chaque sous-coroutine spécifiera un Dispatcher différent si nécessaire.

withContext n'est généralement pas utilisé pour démarrer des sous-coroutines, mais pour changer temporairement le contexte de la coroutine actuelle. Il devrait se terminer dès que son bloc de code sera terminé (à partir de la version 1.3.2, cela est en fait toujours indiqué dans sa documentation). Son cas d'utilisation principal consiste à décharger une opération longue du thread de boucle d'événements (tel que le thread principal de l'interface graphique) vers un Dispatcher qui utilise son propre pool de threads. Un autre cas d'utilisation consiste à définir une "section critique" dans laquelle la coroutine ne réagira pas aux demandes d'annulation.

6
Marko Topolnik