Les coroutines Kotlin offrent-elles des garanties de «survenance avant»?
Par exemple, existe-t-il une garantie «passe avant» entre l'écriture mutableVar
et la lecture ultérieure sur (potentiellement) un autre thread dans ce cas:
suspend fun doSomething() {
var mutableVar = 0
withContext(Dispatchers.IO) {
mutableVar = 1
}
System.out.println("value: $mutableVar")
}
Éditer:
Un exemple supplémentaire clarifiera peut-être mieux la question, car il s'agit davantage de Kotlin-ish (sauf pour la mutabilité). Ce code est-il sûr pour les threads:
suspend fun doSomething() {
var data = withContext(Dispatchers.IO) {
Data(1)
}
System.out.println("value: ${data.data}")
}
private data class Data(var data: Int)
withContext
, tandis que le 1er exemple le crée d'abord, mute à l'intérieurwithContext
, puis lit aprèswithContext
. Ainsi, le 1er exemple exerce plus de fonctionnalités de sécurité des threads.Réponses:
Le code que vous avez écrit a trois accès à l'état partagé:
Les trois accès sont strictement séquentiellement ordonnés, sans concurrence entre eux, et vous pouvez être assuré que l'infrastructure de Kotlin prend en charge l'établissement d'un bord avant lors du transfert vers le
IO
pool de threads et le retour à votre coroutine appelante.Voici un exemple équivalent qui peut peut-être sembler plus convaincant:
Étant donné que
delay
c'est une fonction suspendue et que nous utilisons leDefault
répartiteur soutenu par un pool de threads, les lignes 1, 2 et 3 peuvent chacune s'exécuter sur un thread différent. Par conséquent, votre question sur les garanties avant- propos s'applique également à cet exemple. En revanche, dans ce cas il est (j'espère) tout à fait évident que le comportement de ce code est conforme aux principes d'exécution séquentielle.la source
executorService.submit()
et il existe un mécanisme typique d'attente à la fin de la tâche (terminer une tâcheCompletableFuture
ou quelque chose de similaire). Du point de vue des coroutines Kotlin, il n'y a pas du tout de concurrence ici.Les coroutines de Kotlin prévoient des garanties avant les garanties.
La règle est la suivante: à l'intérieur d'une coroutine, le code avant un appel de fonction de suspension se produit avant le code après l'appel de suspension.
Vous devriez penser aux coroutines comme s'il s'agissait de threads réguliers:
Source: https://proandroiddev.com/what-is-concurrent-access-to-mutable-state-f386e5cb8292
Revenons à l'exemple de code. Capturer des variables dans des corps de fonctions lambda n'est pas la meilleure idée, surtout lorsque lambda est une coroutine. Le code avant un lambda ne se produit pas avant le code à l'intérieur.
Voir https://youtrack.jetbrains.com/issue/KT-15514
la source