Was bedeutet Suspend-Funktion in Kotlin Coroutine?


118

Ich lese Kotlin Coroutine und weiß, dass es auf suspendFunktion basiert . Aber was heißt suspenddas?

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 awaitaufgerufen wird computation, also könnte es sein, asyncdass 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 suspenddie äußere asyncoder suspenddie innere Koroutine?computation Coroutine?

Ist suspendbedeuten , dass während äußere asyncKoroutine wird (Warte await) für den inneren computationKoroutine bis Ende, es (das äußere asyncKoroutine) idles (daher der Name suspendieren) und kehrt Thread zu dem Thread - Pool, und wenn das Kind computationKoroutine beendet, es (das äußere asyncKoroutine ) 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

Antworten:


113

Funktionen aussetzen stehen im Mittelpunkt aller Coroutinen. Eine Suspendierungsfunktion ist einfach eine Funktion, die angehalten und zu einem späteren Zeitpunkt wieder aufgenommen werden kann. Sie können einen lang laufenden Vorgang ausführen und warten, bis er abgeschlossen ist, ohne ihn zu blockieren.

Die Syntax einer Suspending-Funktion ähnelt der einer regulären Funktion, mit Ausnahme des Hinzufügens des suspendSchlüsselworts. Es kann einen Parameter annehmen und einen Rückgabetyp haben. Suspendierungsfunktionen können jedoch nur von einer anderen Suspendierungsfunktion oder innerhalb einer Coroutine aufgerufen werden.

suspend fun backgroundTask(param: Int): Int {
     // long running operation
}

Unter der Haube werden Suspend-Funktionen vom Compiler in eine andere Funktion ohne das Schlüsselwort suspend konvertiert, das einen Additionsparameter vom Typ akzeptiert Continuation<T>. Die obige Funktion wird beispielsweise vom Compiler folgendermaßen konvertiert:

fun backgroundTask(param: Int, callback: Continuation<Int>): Int {
   // long running operation
}

Continuation<T> ist eine Schnittstelle, die zwei Funktionen enthält, die aufgerufen werden, um die Coroutine mit einem Rückgabewert oder mit einer Ausnahme fortzusetzen, wenn beim Anhalten der Funktion ein Fehler aufgetreten ist.

interface Continuation<in T> {
   val context: CoroutineContext
   fun resume(value: T)
   fun resumeWithException(exception: Throwable)
}

4
Ein weiteres Rätsel enthüllt! Toll!
WindRider

15
Ich frage mich, wie diese Funktion tatsächlich angehalten wird. Sie sagen immer, dass suspend fundas angehalten werden kann, aber wie genau?
WindRider

2
@WindRider Es bedeutet nur, dass der aktuelle Thread eine andere Coroutine ausführt und später darauf zurückkommt.
Joffrey

2
Ich habe den "mysteriösen" Mechanismus herausgefunden. Es kann einfach mit Hilfe von Tools> Kotlin> Bytecode> Decompile btn enthüllt werden. Es zeigt, wie der sogenannte "Suspendierungspunkt" implementiert wird - über Fortsetzung und so weiter. Jeder kann selbst einen Blick darauf werfen.
WindRider

4
@buzaa Hier ist ein Vortrag von Roman Elizarov aus dem Jahr 2017, der ihn auf Bytecode-Ebene erklärt.
Marko Topolnik

30

Um zu verstehen, was es genau bedeutet, eine Coroutine auszusetzen, schlage ich vor, dass Sie diesen Code durchgehen:

import kotlinx.coroutines.Dispatchers.Unconfined
import kotlinx.coroutines.launch
import kotlinx.coroutines.runBlocking
import kotlin.coroutines.Continuation
import kotlin.coroutines.resume
import kotlin.coroutines.suspendCoroutine

var continuation: Continuation<Int>? = null

fun main() = runBlocking {
    launch(Unconfined) {
        val a = a()
        println("Result is $a")
    }
    10.downTo(0).forEach {
        continuation!!.resume(it)
    }
}

suspend fun a(): Int {
    return b()
}

suspend fun b(): Int {
    while (true) {
        val i = suspendCoroutine<Int> { cont -> continuation = cont }
        if (i == 0) {
            return 0
        }
    }
}

Der UnconfinedCoroutine-Dispatcher eliminiert die Magie des Coroutine-Dispatchings und ermöglicht es uns, uns direkt auf bloße Coroutinen zu konzentrieren.

Der Code innerhalb des launchBlocks wird als Teil des launchAufrufs sofort auf dem aktuellen Thread ausgeführt . Was passiert ist wie folgt:

  1. Bewerten val a = a()
  2. Diese Ketten b()erreichen suspendCoroutine.
  3. Die Funktion b()führt den an übergebenen Block aus suspendCoroutineund gibt dann einen speziellen COROUTINE_SUSPENDEDWert zurück. Dieser Wert kann durch das Kotlin-Programmiermodell nicht beobachtet werden, aber genau das tut die kompilierte Java-Methode.
  4. Wenn die Funktion a()diesen Rückgabewert sieht, gibt sie ihn auch selbst zurück.
  5. Der launchBlock macht dasselbe und die Steuerung kehrt nach dem launchAufruf zur Zeile zurück :10.downTo(0)...

Beachten Sie, dass Sie zu diesem Zeitpunkt den gleichen Effekt haben, als ob der Code im launchBlock und Ihr fun mainCode gleichzeitig ausgeführt werden. Es kommt einfach vor, dass dies alles auf einem einzigen nativen Thread geschiehtlaunch Block "angehalten" wird.

Jetzt forEachliest das Programm innerhalb des Schleifencodes das continuation, was die b()Funktion geschrieben hat, und resumeses mit dem Wert von 10. resume()wird so implementiert, dass es so aussieht, als würde der suspendCoroutineAufruf mit dem von Ihnen übergebenen Wert zurückgegeben. Sie befinden sich also plötzlich mitten in der Ausführung b(). Der Wert, an den Sie übergeben haben, resume()wird zugewiesen iund überprüft 0. Wenn es nicht Null ist, while (true)geht die Schleife nach innen weiter b()und erreicht wieder suspendCoroutine, an welchem ​​Punkt Ihr resume()Anruf zurückkehrt, und jetzt durchlaufen Sie einen weiteren Schleifenschritt forEach(). Dies geht so lange weiter, bis Sie schließlich fortfahren 0, dann die printlnAnweisung 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 launchAufruf (oder allgemeiner auf den Coroutine Builder ) zurückgesetzt wird. Wenn eine Coroutine nach der Wiederaufnahme erneut angehalten wird, wird der resume()Anruf beendet und die Steuerung kehrt zum Anrufer von zurück resume().

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 continuationObjekt auch, damit er es fortsetzen kann, wenn der Rückgabewert verfügbar ist.


19

Die beste Quelle, um diese IMO zu verstehen, ist der Vortrag "Deep Dive into Coroutines" von Roman Elizarov.

Coroutine oder Funktion wird ausgesetzt?

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.

Welche Coroutine wird suspendiert?

Schauen wir uns Ihren Code an und teilen Sie auf, was passiert:

// 1. this call starts a new coroutine (let's call it C1).
//    If there were code after it, it would be executed concurrently with
//    the body of this async
async {
    ...
    // 2. this is a regular function call
    val deferred = computation()
    // 4. because await() is suspendING, it suspends coroutine C1.
    //    This means that if we had a single thread in our dispatcher, 
    //    it would now be free to go execute C2
    // 7. once C2 completes, C1 is resumed with the result `true` of C2's async
    val result = deferred.await() 
    ...
    // 8. C1 can now keep going in the current thread until it gets 
    //    suspended again (or not)
}

fun computation(): Deferred<Boolean> {
    // 3. this async call starts a second coroutine (C2). Depending on the 
    //    dispatcher you're using, you may have one or more threads.
    // 3.a. If you have multiple threads, the block of this async could be
    //      executed in parallel of C1 in another thread. The control flow 
    //      of the current thread returns to the caller of computation().
    // 3.b. If you have only one thread, the block is sort of "queued" but 
    //      not executed right away, and the control flow returns to the 
    //      caller of computation(). (unless a special dispatcher or 
    //      coroutine start argument is used, but let's keep it simple).
    //    In both cases, we say that this block executes "concurrently"
    //    with C1.
    return async {
        // 5. this may now be executed
        true
        // 6. C2 is now completed, so the thread can go back to executing 
        //    another coroutine (e.g. C1 here)
    }
}

Das Äußere asyncstartet eine Coroutine. Wenn es ruft computation(), asyncstartet das Innere eine zweite Coroutine. Dann wird der Aufruf zum await()Unterbrechen der Ausführung der äußeren async Coroutine fortgesetzt, bis die Ausführung der inneren async Coroutine beendet ist.

Sie können das sogar mit einem einzelnen Thread sehen: Der Thread führt den asyncAnfang des Äußeren aus , ruft dann auf computation()und erreicht den inneren async. Zu diesem Zeitpunkt wird der Körper der inneren Asynchronität übersprungen, und der Thread führt die äußere weiter aus, asyncbis er erreicht ist await(). await()ist ein "Aufhängepunkt", da awaites 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ühren async.

Bedeutet suspendieren, dass während die äußere asynchrone Coroutine auf das Ende der inneren Berechnungskoroutine wartet (wartet), diese (die äußere asynchrone Coroutine) inaktiv ist (daher der Name suspend) und den Thread an den Thread-Pool zurückgibt und wenn die untergeordnete Rechenkoroutine beendet ist , es (die äußere asynchrone Coroutine) wacht auf, nimmt einen weiteren Thread aus dem Pool und fährt fort?

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).


3
Tolle Antwort, ich vermisse diese wirklich grundlegende Erklärung, wenn es um Coroutinen geht.
Bernardo.g

Warum ist das nicht in einer anderen Sprache implementiert? Oder fehlt mir etwas? Ich denke so lange über diese Lösung nach, bin froh, dass Kotlin sie hat, bin mir aber nicht sicher, warum TS oder Rust so etwas haben
PEZO

@ PEZO gut Coroutinen gibt es schon lange. Kotlin hat sie nicht erfunden, aber die Syntax und die Bibliothek bringen sie zum Leuchten. Go hat Goroutinen, JavaScript und TypeScript haben Versprechen. Der einzige Unterschied besteht in den Details der Syntax, um sie zu verwenden. Ich finde es ziemlich ärgerlich / störend async, wenn die Funktionen von JS auf diese Weise markiert werden und dennoch ein Versprechen zurückgeben.
Joffrey

Entschuldigung, mein Kommentar war nicht klar. Ich beziehe mich auf das Schlüsselwort suspend. Es ist nicht dasselbe wie asynchron.
PEZO

Danke, dass du auf Romans Video gezeigt hast. Reines Gold.
Denunzieren Sie

8

Ich habe festgestellt, dass der beste Weg, dies zu verstehen, darin suspendbesteht, eine Analogie zwischen thisSchlüsselwort und coroutineContextEigenschaft herzustellen .

Kotlin-Funktionen können als lokal oder global deklariert werden. Lokale Funktionen haben auf magische Weise Zugriff auf thisSchlüsselwörter, globale nicht.

Kotlin-Funktionen können als suspendoder blockierend deklariert werden . suspendFunktionen haben auf magische Weise Zugriff auf coroutineContextEigentum, während blockierende Funktionen dies nicht tun.

Die Sache ist: coroutineContextEigenschaft wird wie eine "normale" Eigenschaft in Kotlin stdlib deklariert, aber diese Deklaration ist nur ein Stub für Dokumentations- / Navigationszwecke. In der Tat coroutineContextist eine intrinsische Eigenschaft eingebaut , was bedeutet, dass unter der Haube Compiler-Magie diese Eigenschaft ebenso kennt wie Sprachschlüsselwörter.

Das thisSchlüsselwort für lokale Funktionen ist das, was die coroutineContextEigenschaft für suspendFunktionen tut : Es ermöglicht den Zugriff auf den aktuellen Ausführungskontext.

Sie müssen also suspendZugriff auf die coroutineContextEigenschaft erhalten - die Instanz des aktuell ausgeführten Coroutine-Kontexts


5

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 suspendFunktion angehalten werden . Lassen Sie uns dies untersuchen:

In Android könnten wir dies zum Beispiel tun:

var TAG = "myTAG:"
        fun myMethod() { // function A in image
            viewModelScope.launch(Dispatchers.Default) {
                for (i in 10..15) {
                    if (i == 10) { //on first iteration, we will completely FREEZE this coroutine (just for loop here gets 'suspended`)
                        println("$TAG im a tired coroutine - let someone else print the numbers async. i'll suspend until your done")
                        freezePleaseIAmDoingHeavyWork()
                    } else
                        println("$TAG $i")
                    }
            }

            //this area is not suspended, you can continue doing work
        }


        suspend fun freezePleaseIAmDoingHeavyWork() { // function B in image
            withContext(Dispatchers.Default) {
                async {
                    //pretend this is a big network call
                    for (i in 1..10) {
                        println("$TAG $i")
                        delay(1_000)//delay pauses coroutine, NOT the thread. use  Thread.sleep if you want to pause a thread. 
                    }
                    println("$TAG phwww finished printing those numbers async now im tired, thank you for freezing, you may resume")
                }
            }
        }

Der obige Code gibt Folgendes aus:

I: myTAG: my coroutine is frozen but i can carry on to do other things

I: myTAG: im a tired coroutine - let someone else print the numbers async. i'll suspend until your done

I: myTAG: 1
I: myTAG: 2
I: myTAG: 3
I: myTAG: 4
I: myTAG: 5
I: myTAG: 6
I: myTAG: 7
I: myTAG: 8
I: myTAG: 9
I: myTAG: 10

I: myTAG: phwww finished printing those numbers async now im tired, thank you for freezing, you may resume

I: myTAG: 11
I: myTAG: 12
I: myTAG: 13
I: myTAG: 14
I: myTAG: 15

Stellen Sie sich vor, es funktioniert so:

Geben Sie hier die Bildbeschreibung ein

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 aufnehmenonResume

Speichern Sie eine Variable namens continuationund wir laden sie mit dem Coroutines-Fortsetzungsobjekt für uns:

var continuation: CancellableContinuation<String>? = null

suspend fun freezeHere() = suspendCancellableCoroutine<String> {
            continuation = it
        }

 fun unFreeze() {
            continuation?.resume("im resuming") {}
        }

Kehren wir nun zu unserer angehaltenen Funktion zurück und lassen sie mitten in der Iteration einfrieren:

 suspend fun freezePleaseIAmDoingHeavyWork() {
        withContext(Dispatchers.Default) {
            async {
                //pretend this is a big network call
                for (i in 1..10) {
                    println("$TAG $i")
                    delay(1_000)
                    if(i == 3)
                        freezeHere() //dead pause, do not go any further
                }
            }
        }
    }

Dann woanders wie in onResume (zum Beispiel):

override fun onResume() {
        super.onResume()
        unFreeze()
    }

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


4

Da es bereits viele gute Antworten gibt, möchte ich ein einfacheres Beispiel für andere veröffentlichen.

runBlocking Anwendungsfall:

  • myMethod () ist eine suspendFunktion
  • runBlocking { }startet eine Coroutine auf blockierende Weise. Es ähnelt dem Blockieren normaler Threads mit der ThreadKlasse und dem Benachrichtigen blockierter Threads nach bestimmten Ereignissen.
  • runBlocking { }nicht blockieren Thread die aktuelle Ausführung, bis der Koroutine (Körper zwischen {}) wird abgeschlossen

     override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.main_activity)
        Log.i(TAG,"Outer code started on Thread : " + Thread.currentThread().name);
        runBlocking {
            Log.d(TAG,"Inner code started  on Thread : " + Thread.currentThread().name + " making outer code suspend");
            myMethod();
        }
        Log.i(TAG,"Outer code resumed on Thread : " + Thread.currentThread().name);
    }
    
    private suspend fun myMethod() {
        withContext(Dispatchers.Default) {
        for(i in 1..5) {
            Log.d(TAG,"Inner code i : $i on Thread : " + Thread.currentThread().name);
        }
    }

Dies gibt aus:

I/TAG: Outer code started on Thread : main
D/TAG: Inner code started  on Thread : main making outer code suspend
// ---- main thread blocked here, it will wait until coroutine gets completed ----
D/TAG: Inner code i : 1 on Thread : DefaultDispatcher-worker-2
D/TAG: Inner code i : 2 on Thread : DefaultDispatcher-worker-2
D/TAG: Inner code i : 3 on Thread : DefaultDispatcher-worker-2
D/TAG: Inner code i : 4 on Thread : DefaultDispatcher-worker-2
D/TAG: Inner code i : 5 on Thread : DefaultDispatcher-worker-2
// ---- main thread resumes as coroutine is completed ----
I/TAG: Outer code resumed on Thread : main

Anwendungsfall starten :

  • launch { } startet gleichzeitig eine Coroutine.
  • Dies bedeutet, dass beim Festlegen des Starts eine Coroutine die Ausführung im workerThread startet .
  • Der workerThread und der äußere Thread (von dem wir aufgerufen haben launch { }) laufen beide gleichzeitig. Intern kann JVM Preemptive Threading durchführen
  • Wenn mehrere Aufgaben gleichzeitig ausgeführt werden müssen, können wir dies verwenden. Es gibt scopeswelche, die die Lebensdauer der Coroutine angeben. Wenn wir angeben GlobalScope, funktioniert die Coroutine bis zum Ende der Anwendungslebensdauer.

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.main_activity)
        Log.i(TAG,"Outer code started on Thread : " + Thread.currentThread().name);
    
        GlobalScope.launch(Dispatchers.Default) {
            Log.d(TAG,"Inner code started  on Thread : " + Thread.currentThread().name + " making outer code suspend");
            myMethod();
        }
        Log.i(TAG,"Outer code resumed on Thread : " + Thread.currentThread().name);
    }
    
    private suspend fun myMethod() {
        withContext(Dispatchers.Default) {
            for(i in 1..5) {
                Log.d(TAG,"Inner code i : $i on Thread : " + Thread.currentThread().name);
            }
        }
    }

Diese Ausgaben:

10806-10806/com.example.viewmodelapp I/TAG: Outer code started on Thread : main
10806-10806/com.example.viewmodelapp I/TAG: Outer code resumed on Thread : main
// ---- In this example, main had only 2 lines to execute. So, worker thread logs start only after main thread logs complete
// ---- In some cases, where main has more work to do, the worker thread logs get overlap with main thread logs
10806-10858/com.example.viewmodelapp D/TAG: Inner code started  on Thread : DefaultDispatcher-worker-1 making outer code suspend
10806-10858/com.example.viewmodelapp D/TAG: Inner code i : 1 on Thread : DefaultDispatcher-worker-1
10806-10858/com.example.viewmodelapp D/TAG: Inner code i : 2 on Thread : DefaultDispatcher-worker-1
10806-10858/com.example.viewmodelapp D/TAG: Inner code i : 3 on Thread : DefaultDispatcher-worker-1
10806-10858/com.example.viewmodelapp D/TAG: Inner code i : 4 on Thread : DefaultDispatcher-worker-1
10806-10858/com.example.viewmodelapp D/TAG: Inner code i : 5 on Thread : DefaultDispatcher-worker-1

asynchron und warten auf Anwendungsfall:

  • Wenn wir mehrere Aufgaben zu erledigen haben und diese von der Fertigstellung anderer abhängen asyncund awaithelfen würden.
  • Im folgenden Code gibt es beispielsweise die 2Suspend-Funktionen myMethod () und myMethod2 (). myMethod2()sollte erst nach vollständiger Fertigstellung von myMethod() OR ausgeführt werden, myMethod2() hängt vom Ergebnis ab myMethod(), wir können asyncund verwendenawait
  • asyncstartet parallel eine Coroutine ähnlich wie launch. Es bietet jedoch die Möglichkeit, auf eine Coroutine zu warten, bevor eine andere Coroutine parallel gestartet wird.
  • So ist das await(). asyncgibt eine Instanz von zurück Deffered<T>. Twäre Unitfür Standard. Wenn wir für jeden warten müssen async‚s Abschluss, müssen wir Anruf .await()auf Deffered<T>Instanz , dass async. Wie im folgenden Beispiel haben wir aufgerufen, innerAsync.await()was bedeutet, dass die Ausführung angehalten wird, bis sie innerAsyncabgeschlossen ist. Wir können das gleiche in der Ausgabe beobachten. Das innerAsyncwird zuerst erledigt, was anruft myMethod(). Und dann async innerAsync2geht es weiter, was ruftmyMethod2()

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.main_activity)
        Log.i(TAG,"Outer code started on Thread : " + Thread.currentThread().name);
    
         job = GlobalScope.launch(Dispatchers.Default) {
             innerAsync = async {
                 Log.d(TAG, "Inner code started  on Thread : " + Thread.currentThread().name + " making outer code suspend");
                 myMethod();
             }
             innerAsync.await()
    
             innerAsync2 = async {
                 Log.w(TAG, "Inner code started  on Thread : " + Thread.currentThread().name + " making outer code suspend");
                 myMethod2();
             }
        }
    
        Log.i(TAG,"Outer code resumed on Thread : " + Thread.currentThread().name);
        }
    
    private suspend fun myMethod() {
        withContext(Dispatchers.Default) {
            for(i in 1..5) {
                Log.d(TAG,"Inner code i : $i on Thread : " + Thread.currentThread().name);
            }
        }
    }
    
    private suspend fun myMethod2() {
        withContext(Dispatchers.Default) {
            for(i in 1..10) {
                Log.w(TAG,"Inner code i : $i on Thread : " + Thread.currentThread().name);
            }
        }
    }

Dies gibt aus:

11814-11814/? I/TAG: Outer code started on Thread : main
11814-11814/? I/TAG: Outer code resumed on Thread : main
11814-11845/? D/TAG: Inner code started  on Thread : DefaultDispatcher-worker-2 making outer code suspend
11814-11845/? D/TAG: Inner code i : 1 on Thread : DefaultDispatcher-worker-2
11814-11845/? D/TAG: Inner code i : 2 on Thread : DefaultDispatcher-worker-2
11814-11845/? D/TAG: Inner code i : 3 on Thread : DefaultDispatcher-worker-2
11814-11845/? D/TAG: Inner code i : 4 on Thread : DefaultDispatcher-worker-2
11814-11845/? D/TAG: Inner code i : 5 on Thread : DefaultDispatcher-worker-2
// ---- Due to await() call, innerAsync2 will start only after innerAsync gets completed
11814-11848/? W/TAG: Inner code started  on Thread : DefaultDispatcher-worker-4 making outer code suspend
11814-11848/? W/TAG: Inner code i : 1 on Thread : DefaultDispatcher-worker-4
11814-11848/? W/TAG: Inner code i : 2 on Thread : DefaultDispatcher-worker-4
11814-11848/? W/TAG: Inner code i : 3 on Thread : DefaultDispatcher-worker-4
11814-11848/? W/TAG: Inner code i : 4 on Thread : DefaultDispatcher-worker-4
11814-11848/? W/TAG: Inner code i : 5 on Thread : DefaultDispatcher-worker-4
11814-11848/? W/TAG: Inner code i : 6 on Thread : DefaultDispatcher-worker-4
11814-11848/? W/TAG: Inner code i : 7 on Thread : DefaultDispatcher-worker-4
11814-11848/? W/TAG: Inner code i : 8 on Thread : DefaultDispatcher-worker-4
11814-11848/? W/TAG: Inner code i : 9 on Thread : DefaultDispatcher-worker-4
11814-11848/? W/TAG: Inner code i : 10 on Thread : DefaultDispatcher-worker-4
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.