Ich lese Kotlin Coroutine und weiß, dass es auf suspend
Funktion basiert . Aber was heißt suspend
das?
Coroutine oder Funktion wird ausgesetzt?
Von https://kotlinlang.org/docs/reference/coroutines.html
Grundsätzlich sind Coroutinen Berechnungen, die angehalten werden können, ohne einen Thread zu blockieren
Ich hörte Leute oft sagen "Funktion aussetzen". Aber ich denke, es ist die Coroutine, die suspendiert wird, weil sie darauf wartet, dass die Funktion beendet ist? "suspendieren" bedeutet normalerweise "Betrieb einstellen", in diesem Fall ist die Coroutine inaktiv.
🤔 Sollen wir sagen, dass die Coroutine ausgesetzt ist?
Welche Coroutine wird suspendiert?
Von https://kotlinlang.org/docs/reference/coroutines.html
Um die Analogie fortzusetzen, kann await () eine Suspendierungsfunktion sein (daher auch innerhalb eines asynchronen {} Blocks aufrufbar), die eine Coroutine suspendiert, bis eine Berechnung abgeschlossen ist und ihr Ergebnis zurückgibt:
async { // Here I call it the outer async coroutine
...
// Here I call computation the inner coroutine
val result = computation.await()
...
}
🤔 Es heißt "das setzt eine Coroutine aus, bis eine Berechnung abgeschlossen ist", aber Coroutine ist wie ein leichter Thread. Wie kann die Berechnung durchgeführt werden, wenn die Coroutine ausgesetzt ist?
Wir sehen , dass es await
aufgerufen wird computation
, also könnte es sein, async
dass es zurückkehrt Deferred
, was bedeutet, dass es eine andere Coroutine starten kann
fun computation(): Deferred<Boolean> {
return async {
true
}
}
🤔 Das Zitat besagt, dass eine Coroutine suspendiert wird . Bedeutet es suspend
die äußere async
oder suspend
die innere Koroutine?computation
Coroutine?
Ist suspend
bedeuten , dass während äußere async
Koroutine wird (Warte await
) für den inneren computation
Koroutine bis Ende, es (das äußere async
Koroutine) idles (daher der Name suspendieren) und kehrt Thread zu dem Thread - Pool, und wenn das Kind computation
Koroutine beendet, es (das äußere async
Koroutine ) wacht auf, nimmt einen weiteren Thread aus dem Pool und fährt fort?
Der Grund, warum ich den Thread erwähne, ist https://kotlinlang.org/docs/tutorials/coroutines-basic-jvm.html
Der Thread wird an den Pool zurückgegeben, während die Coroutine wartet, und wenn das Warten beendet ist, wird die Coroutine an einem freien Thread im Pool fortgesetzt
quelle
suspend fun
das angehalten werden kann, aber wie genau?Um zu verstehen, was es genau bedeutet, eine Coroutine auszusetzen, schlage ich vor, dass Sie diesen Code durchgehen:
Der
Unconfined
Coroutine-Dispatcher eliminiert die Magie des Coroutine-Dispatchings und ermöglicht es uns, uns direkt auf bloße Coroutinen zu konzentrieren.Der Code innerhalb des
launch
Blocks wird als Teil deslaunch
Aufrufs sofort auf dem aktuellen Thread ausgeführt . Was passiert ist wie folgt:val a = a()
b()
erreichensuspendCoroutine
.b()
führt den an übergebenen Block aussuspendCoroutine
und gibt dann einen speziellenCOROUTINE_SUSPENDED
Wert zurück. Dieser Wert kann durch das Kotlin-Programmiermodell nicht beobachtet werden, aber genau das tut die kompilierte Java-Methode.a()
diesen Rückgabewert sieht, gibt sie ihn auch selbst zurück.launch
Block macht dasselbe und die Steuerung kehrt nach demlaunch
Aufruf zur Zeile zurück :10.downTo(0)...
Beachten Sie, dass Sie zu diesem Zeitpunkt den gleichen Effekt haben, als ob der Code im
launch
Block und Ihrfun main
Code gleichzeitig ausgeführt werden. Es kommt einfach vor, dass dies alles auf einem einzigen nativen Thread geschiehtlaunch
Block "angehalten" wird.Jetzt
forEach
liest das Programm innerhalb des Schleifencodes dascontinuation
, was dieb()
Funktion geschrieben hat, undresumes
es mit dem Wert von10
.resume()
wird so implementiert, dass es so aussieht, als würde dersuspendCoroutine
Aufruf mit dem von Ihnen übergebenen Wert zurückgegeben. Sie befinden sich also plötzlich mitten in der Ausführungb()
. Der Wert, an den Sie übergeben haben,resume()
wird zugewieseni
und überprüft0
. Wenn es nicht Null ist,while (true)
geht die Schleife nach innen weiterb()
und erreicht wiedersuspendCoroutine
, an welchem Punkt Ihrresume()
Anruf zurückkehrt, und jetzt durchlaufen Sie einen weiteren SchleifenschrittforEach()
. Dies geht so lange weiter, bis Sie schließlich fortfahren0
, dann dieprintln
Anweisung ausgeführt wird und das Programm abgeschlossen ist.Die obige Analyse sollte Ihnen die wichtige Intuition vermitteln, dass "Anhalten einer Coroutine" bedeutet, dass das Steuerelement auf den innersten
launch
Aufruf (oder allgemeiner auf den Coroutine Builder ) zurückgesetzt wird. Wenn eine Coroutine nach der Wiederaufnahme erneut angehalten wird, wird derresume()
Anruf beendet und die Steuerung kehrt zum Anrufer von zurückresume()
.Das Vorhandensein eines Coroutine-Dispatchers macht diese Argumentation weniger eindeutig, da die meisten von ihnen Ihren Code sofort an einen anderen Thread senden. In diesem Fall passiert die obige Geschichte in diesem anderen Thread, und der Coroutine-Dispatcher verwaltet das
continuation
Objekt auch, damit er es fortsetzen kann, wenn der Rückgabewert verfügbar ist.quelle
Die beste Quelle, um diese IMO zu verstehen, ist der Vortrag "Deep Dive into Coroutines" von Roman Elizarov.
Anrufen einer suspendieren ing Funktion aussetzen s die Koroutine kann den aktuellen Thread Bedeutung starten eine andere Koroutine ausführt. Die Coroutine soll also eher suspendiert sein als die Funktion.
In der Tat werden Anrufstellen mit Suspendierungsfunktionen aus diesem Grund als "Suspendierungspunkte" bezeichnet.
Schauen wir uns Ihren Code an und teilen Sie auf, was passiert:
Das Äußere
async
startet eine Coroutine. Wenn es ruftcomputation()
,async
startet das Innere eine zweite Coroutine. Dann wird der Aufruf zumawait()
Unterbrechen der Ausführung der äußerenasync
Coroutine fortgesetzt, bis die Ausführung der innerenasync
Coroutine beendet ist.Sie können das sogar mit einem einzelnen Thread sehen: Der Thread führt den
async
Anfang des Äußeren aus , ruft dann aufcomputation()
und erreicht den innerenasync
. Zu diesem Zeitpunkt wird der Körper der inneren Asynchronität übersprungen, und der Thread führt die äußere weiter aus,async
bis er erreicht istawait()
.await()
ist ein "Aufhängepunkt", daawait
es sich um eine Aufhängungsfunktion handelt. Dies bedeutet, dass die äußere Coroutine aufgehängt ist und der Thread damit beginnt, die innere auszuführen. Wenn es fertig ist, kommt es zurück, um das Ende des Äußeren auszuführenasync
.Ja genau.
Dies wird tatsächlich erreicht, indem jede Suspendierungsfunktion in eine Zustandsmaschine umgewandelt wird, wobei jeder "Zustand" einem Suspendierungspunkt innerhalb dieser Suspendierungsfunktion entspricht. Unter der Haube kann die Funktion mehrmals aufgerufen werden, mit der Information, von welchem Aufhängepunkt aus sie ausgeführt werden soll (Sie sollten sich das von mir verlinkte Video wirklich ansehen, um weitere Informationen dazu zu erhalten).
quelle
async
, wenn die Funktionen von JS auf diese Weise markiert werden und dennoch ein Versprechen zurückgeben.Ich habe festgestellt, dass der beste Weg, dies zu verstehen, darin
suspend
besteht, eine Analogie zwischenthis
Schlüsselwort undcoroutineContext
Eigenschaft herzustellen .Kotlin-Funktionen können als lokal oder global deklariert werden. Lokale Funktionen haben auf magische Weise Zugriff auf
this
Schlüsselwörter, globale nicht.Kotlin-Funktionen können als
suspend
oder blockierend deklariert werden .suspend
Funktionen haben auf magische Weise Zugriff aufcoroutineContext
Eigentum, während blockierende Funktionen dies nicht tun.Die Sache ist:
coroutineContext
Eigenschaft wird wie eine "normale" Eigenschaft in Kotlin stdlib deklariert, aber diese Deklaration ist nur ein Stub für Dokumentations- / Navigationszwecke. In der TatcoroutineContext
ist eine intrinsische Eigenschaft eingebaut , was bedeutet, dass unter der Haube Compiler-Magie diese Eigenschaft ebenso kennt wie Sprachschlüsselwörter.Das
this
Schlüsselwort für lokale Funktionen ist das, was diecoroutineContext
Eigenschaft fürsuspend
Funktionen tut : Es ermöglicht den Zugriff auf den aktuellen Ausführungskontext.Sie müssen also
suspend
Zugriff auf diecoroutineContext
Eigenschaft erhalten - die Instanz des aktuell ausgeführten Coroutine-Kontextsquelle
Ich wollte Ihnen ein einfaches Beispiel für das Konzept der Fortsetzung geben. Dies ist, was eine Suspend-Funktion tut, sie kann einfrieren / suspendieren und dann fortgesetzt / fortgesetzt. Hör auf, an Coroutine in Bezug auf Threads und Semaphore zu denken. Denken Sie an Fortsetzung und sogar an Rückruf-Hooks.
Um klar zu sein, kann eine Coroutine mithilfe einer
suspend
Funktion angehalten werden . Lassen Sie uns dies untersuchen:In Android könnten wir dies zum Beispiel tun:
Der obige Code gibt Folgendes aus:
Stellen Sie sich vor, es funktioniert so:
Die aktuelle Funktion, von der aus Sie gestartet haben, wird also nicht gestoppt. Nur eine Coroutine wird angehalten, während sie fortgesetzt wird. Der Thread wird nicht durch Ausführen einer Suspend-Funktion angehalten.
Ich denke, diese Seite kann Ihnen helfen , die Dinge zu klären, und ist meine Referenz.
Lassen Sie uns etwas Cooles tun und unsere Suspend-Funktion mitten in einer Iteration einfrieren. Wir werden es später wieder aufnehmen
onResume
Speichern Sie eine Variable namens
continuation
und wir laden sie mit dem Coroutines-Fortsetzungsobjekt für uns:Kehren wir nun zu unserer angehaltenen Funktion zurück und lassen sie mitten in der Iteration einfrieren:
Dann woanders wie in onResume (zum Beispiel):
Und die Schleife wird fortgesetzt. Es ist ziemlich ordentlich zu wissen, dass wir eine Suspend-Funktion jederzeit einfrieren und nach einiger Zeit wieder aufnehmen können. Sie können auch in Kanäle schauen
quelle
Da es bereits viele gute Antworten gibt, möchte ich ein einfacheres Beispiel für andere veröffentlichen.
suspend
FunktionrunBlocking { }
startet eine Coroutine auf blockierende Weise. Es ähnelt dem Blockieren normaler Threads mit derThread
Klasse und dem Benachrichtigen blockierter Threads nach bestimmten Ereignissen.runBlocking { }
nicht blockieren Thread die aktuelle Ausführung, bis der Koroutine (Körper zwischen{}
) wird abgeschlossenDies gibt aus:
launch { }
startet gleichzeitig eine Coroutine.worker
Thread startet .worker
Thread und der äußere Thread (von dem wir aufgerufen habenlaunch { }
) laufen beide gleichzeitig. Intern kann JVM Preemptive Threading durchführenWenn mehrere Aufgaben gleichzeitig ausgeführt werden müssen, können wir dies verwenden. Es gibt
scopes
welche, die die Lebensdauer der Coroutine angeben. Wenn wir angebenGlobalScope
, funktioniert die Coroutine bis zum Ende der Anwendungslebensdauer.Diese Ausgaben:
async
undawait
helfen würden.2
Suspend-Funktionen myMethod () und myMethod2 ().myMethod2()
sollte erst nach vollständiger Fertigstellung vonmyMethod()
OR ausgeführt werden,myMethod2()
hängt vom Ergebnis abmyMethod()
, wir könnenasync
und verwendenawait
async
startet parallel eine Coroutine ähnlich wielaunch
. Es bietet jedoch die Möglichkeit, auf eine Coroutine zu warten, bevor eine andere Coroutine parallel gestartet wird.So ist das
await()
.async
gibt eine Instanz von zurückDeffered<T>
.T
wäreUnit
für Standard. Wenn wir für jeden warten müssenasync
‚s Abschluss, müssen wir Anruf.await()
aufDeffered<T>
Instanz , dassasync
. Wie im folgenden Beispiel haben wir aufgerufen,innerAsync.await()
was bedeutet, dass die Ausführung angehalten wird, bis sieinnerAsync
abgeschlossen ist. Wir können das gleiche in der Ausgabe beobachten. DasinnerAsync
wird zuerst erledigt, was anruftmyMethod()
. Und dannasync
innerAsync2
geht es weiter, was ruftmyMethod2()
Dies gibt aus:
quelle