In der kotlinx.coroutines
Bibliothek 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.coroutines
Bibliothek können Sie eine neue Coroutine entweder mit launch
(mit join
) oder async
(mit await
) starten . Was ist der Unterschied zwischen ihnen?
Antworten:
launch
wird verwendet, um Coroutine zu feuern und zu vergessen . Es ist wie ein neuer Thread. Wenn der Code in der launch
Datei 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. join
wird 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.
async
wird verwendet, um eine Coroutine zu starten, die ein Ergebnis berechnet . Das Ergebnis wird durch eine Instanz dargestellt Deferred
und Sie müssen verwenden await
darauf. Eine nicht erfasste Ausnahme im async
Code wird im Ergebnis gespeichert Deferred
und 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 launch
startet einen Hintergrund - Thread, etwas tut, und gibt eine sofort als Token Job
. Sie können dies aufrufen, join
um Job
zu blockieren, bis dieser launch
Thread 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 async
startet 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 Deferred
ist 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.
launch
und async
werden 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 btnCount
Knopfdruck pgBar
herunter und zeigt den Fortschrittsbalken an, bis der gesamte Download abgeschlossen ist. Es gibt 3 suspend
Funktionen 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 seconds
und 5 seconds
jeweils.
Da wir launch
diese Suspend-Funktionen zum Starten verwendet haben, launch
werden 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 Toast
Gesamtausführungszeit zum Abschließen aller 3 Downloads zu 5 Sekunden + 8 Sekunden + 5 Sekunden = 18 Sekunden mit führenlaunch
- asynchron
Wie wir gesehen haben, launch
ist die Ausführung sequentially
fü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
.
async
Gibt eine Instanz vom Deffered<T>
Typ zurück, wobei T
es 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 async
Typ verwenden Deferred<T>
, um den zurückgegebenen Wert vom Typ T
abzurufen. 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 seconds
die Zeit, für downloadTask2()
die es die größte aller drei Aufgaben ist. Sie können dies im folgenden Screenshot in sehenToast message
launch
für sequentielle Spaß ist, während async
für gleichzeitige
launch
und async
wird neue Coroutinen starten. Sie vergleichen eine einzelne Coroutine ohne Kinder mit einer einzelnen Coroutine mit 3 Kindern. Sie könnten jeden der async
Aufrufe durch ersetzen, launch
und 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