In der kotlinx.coroutinesBibliothek können Sie eine neue Coroutine entweder mit launch(mit join) oder async(mit await) starten . Was ist der Unterschied zwischen ihnen?
In der kotlinx.coroutinesBibliothek können Sie eine neue Coroutine entweder mit launch(mit join) oder async(mit await) starten . Was ist der Unterschied zwischen ihnen?
Antworten:
launchwird verwendet, um Coroutine zu feuern und zu vergessen . Es ist wie ein neuer Thread. Wenn der Code in der launchDatei mit Ausnahme beendet wird, wird er in einem Thread wie eine nicht erfasste Ausnahme behandelt - normalerweise in JVM-Backend-Anwendungen auf stderr gedruckt und stürzt Android-Anwendungen ab. joinwird verwendet, um auf den Abschluss der gestarteten Coroutine zu warten und ihre Ausnahme nicht weiterzugeben. Allerdings ist ein abgestürzt Kind bricht Koroutine seine Eltern mit der entsprechenden Ausnahme, auch.
asyncwird verwendet, um eine Coroutine zu starten, die ein Ergebnis berechnet . Das Ergebnis wird durch eine Instanz dargestellt Deferredund Sie müssen verwenden awaitdarauf. Eine nicht erfasste Ausnahme im asyncCode wird im Ergebnis gespeichert Deferredund nirgendwo anders geliefert. Sie wird stillschweigend gelöscht, sofern sie nicht verarbeitet wird. Sie dürfen die Coroutine, die Sie mit Async begonnen haben, NICHT vergessen .
Ich finde diesen Leitfaden https://github.com/Kotlin/kotlinx.coroutines/blob/master/coroutines-guide.md nützlich. Ich werde die wesentlichen Teile zitieren
🦄 Coroutine
Coroutinen sind im Wesentlichen leichte Fäden.
Sie können sich Coroutine also als etwas vorstellen, das Threads auf sehr effiziente Weise verwaltet.
🐤 starten
fun main(args: Array<String>) {
launch { // launch new coroutine in background and continue
delay(1000L) // non-blocking delay for 1 second (default time unit is ms)
println("World!") // print after delay
}
println("Hello,") // main thread continues while coroutine is delayed
Thread.sleep(2000L) // block main thread for 2 seconds to keep JVM alive
}
So launchstartet einen Hintergrund - Thread, etwas tut, und gibt eine sofort als Token Job. Sie können dies aufrufen, joinum Jobzu blockieren, bis dieser launchThread abgeschlossen ist
fun main(args: Array<String>) = runBlocking<Unit> {
val job = launch { // launch new coroutine and keep a reference to its Job
delay(1000L)
println("World!")
}
println("Hello,")
job.join() // wait until child coroutine completes
}
🦆 async
Konzeptionell ist Async wie ein Start. Es wird eine separate Coroutine gestartet, bei der es sich um einen leichten Faden handelt, der gleichzeitig mit allen anderen Coroutinen arbeitet. Der Unterschied besteht darin, dass der Start einen Job zurückgibt und keinen resultierenden Wert enthält, während Async einen verzögerten Wert zurückgibt - eine leichte, nicht blockierende Zukunft, die das Versprechen darstellt, später ein Ergebnis bereitzustellen.
So asyncstartet einen Hintergrund - Thread, etwas tut, und gibt eine sofort als Token Deferred.
fun main(args: Array<String>) = runBlocking<Unit> {
val time = measureTimeMillis {
val one = async { doSomethingUsefulOne() }
val two = async { doSomethingUsefulTwo() }
println("The answer is ${one.await() + two.await()}")
}
println("Completed in $time ms")
}
Sie können .await () für einen zurückgestellten Wert verwenden, um das endgültige Ergebnis zu erhalten. Zurückgestellt ist jedoch auch ein Job, sodass Sie ihn bei Bedarf abbrechen können.
Also Deferredist eigentlich ein Job. Siehe https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.experimental/-deferred/index.html
interface Deferred<out T> : Job (source)
🦋 Async ist standardmäßig eifrig
Es gibt eine Faulheitsoption zum Asynchronisieren mit einem optionalen Startparameter mit dem Wert CoroutineStart.LAZY. Die Coroutine wird nur gestartet, wenn das Ergebnis von einigen Wartenden benötigt wird oder wenn eine Startfunktion aufgerufen wird.
launchund asyncwerden verwendet, um neue Coroutinen zu starten. Sie führen sie jedoch auf unterschiedliche Weise aus.
Ich möchte ein sehr einfaches Beispiel zeigen, das Ihnen hilft, Unterschiede sehr leicht zu verstehen
- starten
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
btnCount.setOnClickListener {
pgBar.visibility = View.VISIBLE
CoroutineScope(Dispatchers.Main).launch {
val currentMillis = System.currentTimeMillis()
val retVal1 = downloadTask1()
val retVal2 = downloadTask2()
val retVal3 = downloadTask3()
Toast.makeText(applicationContext, "All tasks downloaded! ${retVal1}, ${retVal2}, ${retVal3} in ${(System.currentTimeMillis() - currentMillis)/1000} seconds", Toast.LENGTH_LONG).show();
pgBar.visibility = View.GONE
}
}
// Task 1 will take 5 seconds to complete download
private suspend fun downloadTask1() : String {
kotlinx.coroutines.delay(5000);
return "Complete";
}
// Task 1 will take 8 seconds to complete download
private suspend fun downloadTask2() : Int {
kotlinx.coroutines.delay(8000);
return 100;
}
// Task 1 will take 5 seconds to complete download
private suspend fun downloadTask3() : Float {
kotlinx.coroutines.delay(5000);
return 4.0f;
}
}
In diesem Beispiel lädt mein Code 3 Daten per btnCountKnopfdruck pgBarherunter und zeigt den Fortschrittsbalken an, bis der gesamte Download abgeschlossen ist. Es gibt 3 suspendFunktionen downloadTask1(), downloadTask2()und downloadTask3()welche die Download - Daten. Um es zu simulieren, habe ich delay()in diesen Funktionen verwendet. Diese Funktionen warten 5 seconds, 8 secondsund 5 secondsjeweils.
Da wir launchdiese Suspend-Funktionen zum Starten verwendet haben, launchwerden sie nacheinander (einzeln) ausgeführt . Dies bedeutet, dass downloadTask2()der Start nach downloadTask1()Abschluss und downloadTask3()erst nach downloadTask2()Abschluss beginnen würde.
Wie im Ausgabe-Screenshot würde die ToastGesamtausführungszeit zum Abschließen aller 3 Downloads zu 5 Sekunden + 8 Sekunden + 5 Sekunden = 18 Sekunden mit führenlaunch
- asynchron
Wie wir gesehen haben, launchist die Ausführung sequentiallyfür alle 3 Aufgaben möglich. Die Zeit, um alle Aufgaben zu erledigen, war 18 seconds.
Wenn diese Aufgaben unabhängig sind und das Berechnungsergebnis anderer Aufgaben nicht benötigen, können sie ausgeführt werden concurrently. Sie würden zur gleichen Zeit starten und gleichzeitig im Hintergrund laufen. Dies kann mit gemacht werden async.
asyncGibt eine Instanz vom Deffered<T>Typ zurück, wobei Tes sich um den Datentyp handelt, den unsere Suspend-Funktion zurückgibt. Beispielsweise,
downloadTask1()würde zurückgeben, Deferred<String>da String der Rückgabetyp der Funktion istdownloadTask2()würde zurückkehren, Deferred<Int>da Int der Rückgabetyp der Funktion istdownloadTask3()würde zurückkehren, Deferred<Float>da Float der Funktionstyp return istWir können das Rückgabeobjekt vom asyncTyp verwenden Deferred<T>, um den zurückgegebenen Wert vom Typ Tabzurufen. Das kann mit await()Anruf gemacht werden. Überprüfen Sie zum Beispiel den folgenden Code
btnCount.setOnClickListener {
pgBar.visibility = View.VISIBLE
CoroutineScope(Dispatchers.Main).launch {
val currentMillis = System.currentTimeMillis()
val retVal1 = async(Dispatchers.IO) { downloadTask1() }
val retVal2 = async(Dispatchers.IO) { downloadTask2() }
val retVal3 = async(Dispatchers.IO) { downloadTask3() }
Toast.makeText(applicationContext, "All tasks downloaded! ${retVal1.await()}, ${retVal2.await()}, ${retVal3.await()} in ${(System.currentTimeMillis() - currentMillis)/1000} seconds", Toast.LENGTH_LONG).show();
pgBar.visibility = View.GONE
}
Auf diese Weise haben wir alle drei Aufgaben gleichzeitig gestartet. Meine gesamte Ausführungszeit wäre also nur 8 secondsdie Zeit, für downloadTask2()die es die größte aller drei Aufgaben ist. Sie können dies im folgenden Screenshot in sehenToast message
launchfür sequentielle Spaß ist, während asyncfür gleichzeitige
launchund asyncwird neue Coroutinen starten. Sie vergleichen eine einzelne Coroutine ohne Kinder mit einer einzelnen Coroutine mit 3 Kindern. Sie könnten jeden der asyncAufrufe durch ersetzen, launchund in Bezug auf die Parallelität würde sich absolut nichts ändern.
Beide Coroutine-Builder, nämlich launch und async, sind im Grunde genommen Lambdas mit einem Empfänger vom Typ CoroutineScope, was bedeutet, dass ihr innerer Block als Suspend-Funktion kompiliert wird. Daher werden beide in einem asynchronen Modus ausgeführt UND beide führen ihren Block nacheinander aus.
Der Unterschied zwischen Start und Async besteht darin, dass sie zwei verschiedene Möglichkeiten ermöglichen. Der Launch Builder gibt einen Job zurück, die asynchrone Funktion gibt jedoch ein zurückgestelltes Objekt zurück. Sie können launch verwenden, um einen Block auszuführen, von dem Sie keinen zurückgegebenen Wert erwarten, dh in eine Datenbank schreiben oder eine Datei speichern oder etwas verarbeiten, das im Grunde nur als Nebeneffekt bezeichnet wird. Auf der anderen Seite gibt Async, das ein Zurückgestelltes zurückgibt, wie ich zuvor angegeben habe, einen nützlichen Wert aus der Ausführung seines Blocks zurück, ein Objekt, das Ihre Daten umschließt, sodass Sie es hauptsächlich für das Ergebnis, möglicherweise aber auch für die Nebenwirkung verwenden können. NB: Sie können den verzögerten Wert entfernen und seinen Wert mit der Funktion wait abrufen, die die Ausführung Ihrer Anweisungen blockiert, bis ein Wert zurückgegeben oder eine Ausnahme ausgelöst wird!
Beide Coroutine Builder (Start und Async) können abgebrochen werden.
Noch etwas?: Ja, wenn beim Start eine Ausnahme innerhalb ihres Blocks ausgelöst wird, wird die Coroutine automatisch abgebrochen und die Ausnahmen werden zugestellt. Wenn dies andererseits mit Async geschieht, wird die Ausnahme nicht weiter verbreitet und sollte innerhalb des zurückgegebenen zurückgestellten Objekts abgefangen / behandelt werden.
Weitere Informationen zu Coroutinen finden Sie unter https://kotlinlang.org/docs/tutorials/coroutines/coroutines-basic-jvm.html https://www.codementor.io/blog/kotlin-coroutines-6n53p8cbn1
Start gibt einen Job zurück
async gibt ein Ergebnis zurück (verzögerter Job)
Beim Starten mit Join wird gewartet, bis der Job abgeschlossen ist. Der Coroutine-Aufruf join () wird einfach angehalten, sodass der aktuelle Thread in der Zwischenzeit andere Arbeiten ausführen kann (z. B. das Ausführen einer anderen Coroutine).
Async wird verwendet, um einige Ergebnisse zu berechnen. Es erstellt eine Coroutine und gibt das zukünftige Ergebnis als Implementierung von Deferred zurück. Die laufende Coroutine wird abgebrochen, wenn die resultierende Verzögerung abgebrochen wird.
Stellen Sie sich eine asynchrone Methode vor, die einen Zeichenfolgenwert zurückgibt. Wenn die asynchrone Methode ohne Warten verwendet wird, wird eine verzögerte Zeichenfolge zurückgegeben. Wenn jedoch Warten verwendet wird, erhalten Sie als Ergebnis eine Zeichenfolge
Der Hauptunterschied zwischen Async und Start. Deferred gibt einen bestimmten Wert vom Typ T zurück, nachdem Ihre Coroutine die Ausführung abgeschlossen hat, Job hingegen nicht.
Async vs Launch Async vs Launch Diff Image
Start / Async kein Ergebnis
asynchron für Ergebnis