Je lis Kotlin Coroutine et je sais qu'il est basé sur la suspend
fonction. Mais qu'est-ce que ça suspend
veut dire?
Coroutine ou fonction suspendue?
De https://kotlinlang.org/docs/reference/coroutines.html
Fondamentalement, les coroutines sont des calculs qui peuvent être suspendus sans bloquer un thread
J'ai souvent entendu les gens dire "suspendre la fonction". Mais je pense que c'est la coroutine qui est suspendue car elle attend que la fonction soit terminée? "suspendre" signifie généralement "arrêter l'opération", dans ce cas la coroutine est inactive.
🤔 Doit-on dire que la coroutine est suspendue?
Quelle coroutine est suspendue?
De https://kotlinlang.org/docs/reference/coroutines.html
Pour continuer l'analogie, await () peut être une fonction de suspension (donc également appelable depuis un bloc async {}) qui suspend une coroutine jusqu'à ce qu'un calcul soit fait et renvoie son résultat:
async { // Here I call it the outer async coroutine
...
// Here I call computation the inner coroutine
val result = computation.await()
...
}
🤔 Il dit "qui suspend une coroutine jusqu'à ce qu'un calcul soit fait", mais la coroutine est comme un thread léger. Donc, si la coroutine est suspendue, comment faire le calcul?
Nous voyons await
est appelé computation
, donc il se peut que async
cela retourne Deferred
, ce qui signifie qu'il peut démarrer une autre coroutine
fun computation(): Deferred<Boolean> {
return async {
true
}
}
🤔 La citation dit que suspend une coroutine . Cela signifie-t-il suspend
la async
coroutine externe ou suspend
la computation
coroutine interne ?
Cela suspend
signifie que pendant que la async
coroutine externe attend ( await
) que la computation
coroutine interne se termine, elle (la async
coroutine externe ) est inactive (d'où le nom suspend) et renvoie le thread au pool de threads, et lorsque la computation
coroutine enfant se termine, elle (la async
coroutine externe) ) se réveille, prend un autre thread du pool et continue?
La raison pour laquelle je mentionne le fil est à cause de https://kotlinlang.org/docs/tutorials/coroutines-basic-jvm.html
Le thread est renvoyé dans le pool pendant que la coroutine est en attente, et lorsque l'attente est terminée, la coroutine reprend sur un thread libre dans le pool
la source
suspend fun
peut être mis en pause, mais comment exactement?Pour comprendre exactement ce que signifie suspendre une coroutine, je vous suggère de parcourir ce code:
Le
Unconfined
répartiteur de coroutine élimine la magie de la répartition de coroutine et nous permet de nous concentrer directement sur les coroutines nues.Le code à l'intérieur du
launch
bloc commence à s'exécuter immédiatement sur le thread actuel, dans le cadre de l'launch
appel. Voici ce qui se passe:val a = a()
b()
, atteignantsuspendCoroutine
.b()
exécute le bloc passé àsuspendCoroutine
, puis renvoie uneCOROUTINE_SUSPENDED
valeur spéciale . Cette valeur n'est pas observable via le modèle de programmation Kotlin, mais c'est ce que fait la méthode Java compilée.a()
, voyant cette valeur de retour, la renvoie également elle-même.launch
bloc fait de même et le contrôle retourne maintenant à la ligne après l'launch
invocation:10.downTo(0)...
Notez qu'à ce stade, vous avez le même effet que si le code à l'intérieur du
launch
bloc et votrefun main
code s'exécutaient simultanément. Il se trouve que tout cela se passe sur un seul thread natif, de sorte que lelaunch
bloc est "suspendu".Maintenant, à l'intérieur du
forEach
code en boucle, le programme lit lecontinuation
que lab()
fonction a écrit etresumes
avec la valeur de10
.resume()
est implémenté de telle manière que ce sera comme si l'suspendCoroutine
appel renvoyé avec la valeur que vous avez transmise. Vous vous trouvez donc soudainement au milieu de l'exécutionb()
. La valeur que vous avez transmiseresume()
est attribuéei
et vérifiée0
. Si ce n'est pas zéro, lawhile (true)
boucle continue à l'intérieurb()
, atteignant à nouveaususpendCoroutine
, à quel point votreresume()
appel revient, et maintenant vous passez par une autre étape de boucleforEach()
. Cela continue jusqu'à ce que vous repreniez enfin avec0
, puis l'println
instruction s'exécute et le programme se termine.L'analyse ci-dessus devrait vous donner l'intuition importante que «suspendre une coroutine» signifie renvoyer le contrôle à l'
launch
invocation la plus interne (ou, plus généralement, au constructeur de coroutine ). Si une coroutine se suspend à nouveau après la reprise, l'resume()
appel se termine et le contrôle revient à l'appelant deresume()
.La présence d'un répartiteur de coroutine rend ce raisonnement moins clair car la plupart d'entre eux soumettent immédiatement votre code à un autre thread. Dans ce cas, l'histoire ci-dessus se produit dans cet autre thread, et le répartiteur de coroutine gère également l'
continuation
objet afin qu'il puisse le reprendre lorsque la valeur de retour est disponible.la source
Tout d'abord, la meilleure source pour comprendre cette OMI est le discours "Deep Dive into Coroutines" de Roman Elizarov.
L' appel d' une suspension ing fonction de suspension de la coroutine, qui signifie que le thread en cours peut commencer à exécuter une autre coroutine. Ainsi, on dit que la coroutine est suspendue plutôt que la fonction.
En fait, les sites d'appel des fonctions de suspension sont appelés «points de suspension» pour cette raison.
Regardons votre code et décomposons ce qui se passe:
L'extérieur
async
commence une coroutine. Lorsqu'il appellecomputation()
, l'interneasync
démarre une seconde coroutine. Ensuite, l'appel àawait()
suspend l'exécution de la coroutine externeasync
, jusqu'à ce que l'exécution de la coroutine interneasync
soit terminée.Vous pouvez même voir cela avec un seul thread: le thread exécutera le
async
début de l'extérieur , puis appelleracomputation()
et atteindra l'intérieurasync
. À ce stade, le corps de l'asynchrone interne est ignoré et le thread continue d'exécuter l'externeasync
jusqu'à ce qu'il atteigneawait()
.await()
est un "point de suspension", carawait
c'est une fonction de suspension. Cela signifie que la coroutine externe est suspendue, et ainsi le thread commence à exécuter la coroutine interne. Quand c'est fait, il revient pour exécuter la fin de l'extérieurasync
.Oui, précisément.
La manière dont cela est réellement réalisé est de transformer chaque fonction de suspension en une machine à états, où chaque "état" correspond à un point de suspension à l'intérieur de cette fonction de suspension. Sous le capot, la fonction peut être appelée plusieurs fois, avec des informations sur le point de suspension à partir duquel elle doit commencer à s'exécuter (vous devriez vraiment regarder la vidéo que j'ai liée pour plus d'informations à ce sujet).
la source
async
fonctions de JS soient marquées de cette façon tout en retournant une promesse.J'ai trouvé que la meilleure façon de comprendre
suspend
est de faire une analogie entrethis
mot-clé etcoroutineContext
propriété.Les fonctions Kotlin peuvent être déclarées comme locales ou globales. Les fonctions locales ont comme par magie accès aux
this
mots-clés, contrairement aux fonctions globales.Les fonctions Kotlin peuvent être déclarées comme
suspend
ou bloquantes.suspend
les fonctions ont par magie accès à lacoroutineContext
propriété alors que les fonctions de blocage ne le font pas.Le fait est que la
coroutineContext
propriété est déclarée comme une propriété "normale" dans Kotlin stdlib mais cette déclaration n'est qu'un stub à des fins de documentation / navigation. En fait,coroutineContext
c'est une propriété intrinsèque intégrée qui signifie que sous le capot, la magie du compilateur est consciente de cette propriété comme elle est consciente des mots-clés du langage.Ce que fait le
this
mot-clé pour les fonctions locales, c'est ce que lacoroutineContext
propriété fait pour lessuspend
fonctions: il donne accès au contexte actuel d'exécution.Donc, vous devez
suspend
obtenir un accès à lacoroutineContext
propriété - l'instance du contexte coroutine actuellement exécutéla source
Je voulais vous donner un exemple simple du concept de continuation. C'est ce que fait une fonction de suspension, elle peut se figer / suspendre puis elle continue / reprend. Arrêtez de penser à la coroutine en termes de threads et de sémaphore. Pensez-y en termes de continuation et même de hooks de rappel.
Pour être clair, une coroutine peut être mise en pause à l'aide d'une
suspend
fonction. examinons ceci:Dans Android, nous pourrions le faire par exemple:
Le code ci-dessus imprime ce qui suit:
imaginez que cela fonctionne comme ceci:
Ainsi, la fonction actuelle que vous avez lancée ne s'arrête pas, juste une coroutine serait suspendue pendant qu'elle continue. Le thread n'est pas interrompu par l'exécution d'une fonction de suspension.
Je pense que ce site peut vous aider et c'est ma référence.
Faisons quelque chose de cool et figeons notre fonction de suspension au milieu d'une itération. Nous le reprendrons plus tard dans
onResume
Stockez une variable appelée
continuation
et nous la chargerons avec l'objet de continuation coroutines pour nous:Maintenant, revenons à notre fonction suspendue et faisons-la geler au milieu de l'itération:
Puis ailleurs comme dans onResume (par exemple):
Et la boucle continuera. Il est assez intéressant de savoir que nous pouvons geler une fonction de suspension à tout moment et la reprendre après un certain temps. Vous pouvez également consulter les chaînes
la source
Comme de nombreuses bonnes réponses sont déjà là, je voudrais poster un exemple plus simple pour les autres.
suspend
fonctionrunBlocking { }
démarre une Coroutine de manière bloquante. C'est similaire à la façon dont nous bloquions les threads normaux avec laThread
classe et notifions les threads bloqués après certains événements.runBlocking { }
Does bloc le fil exécution en cours, jusqu'à ce que le coroutine (corps entre{}
) se rempliCela produit:
launch { }
démarre une coroutine simultanément.worker
thread.worker
thread et le thread externe (à partir desquels nous avons appelélaunch { }
) s'exécutent tous deux simultanément. En interne, JVM peut effectuer threads préventifsLorsque nous avons besoin de plusieurs tâches pour s'exécuter en parallèle, nous pouvons l'utiliser. Il y en a
scopes
qui spécifient la durée de vie de la coroutine. Si nous spécifionsGlobalScope
, la coroutine fonctionnera jusqu'à la fin de la durée de vie de l'application.Ce produit:
async
etawait
cela aiderait.2
fonctions de suspension myMethod () et myMethod2 ().myMethod2()
devrait être exécuté seulement après l'achèvement complet demyMethod()
ORmyMethod2()
dépend du résultat demyMethod()
, nous pouvons utiliserasync
etawait
async
démarre une coroutine en parallèle similaire àlaunch
. Mais, cela fournit un moyen d'attendre une coroutine avant de démarrer une autre coroutine en parallèle.De cette façon
await()
.async
renvoie une instance deDeffered<T>
.T
seraitUnit
par défaut. Quand nous avons besoin d'attendre pour toutasync
l » achèvement, nous avons besoin de faire appel.await()
àDeffered<T>
instance de cetteasync
. Comme dans l'exemple ci-dessous, nous avons appeléinnerAsync.await()
ce qui implique que l'exécution serait suspendue jusqu'à ce qu'elleinnerAsync
soit terminée. Nous pouvons observer la même chose en sortie. LeinnerAsync
est terminé en premier, qui appellemyMethod()
. Et puisasync
innerAsync2
commence ensuite, qui appellemyMethod2()
Cela produit:
la source