Ich lerne Kotlin-Koroutinen. Ich habe das gelesen runBlocking ist der Weg, synchronen und asynchronen Code zu überbrücken. Aber was ist der Leistungsgewinn, wenn die runBlocking stoppt den UI-Thread? Zum Beispiel muss ich eine Datenbank in Android abfragen:
val result: Int
get() = runBlocking { queryDatabase().await() }
private fun queryDatabase(): Deferred<Int> {
return async {
var cursor: Cursor? = null
var queryResult: Int = 0
val sqlQuery = "SELECT COUNT(ID) FROM TABLE..."
try {
cursor = getHelper().readableDatabase.query(sqlQuery)
cursor?.moveToFirst()
queryResult = cursor?.getInt(0) ?: 0
} catch (e: Exception) {
Log.e(TAG, e.localizedMessage)
} finally {
cursor?.close()
}
[email protected] queryResult
}
}
Das Abfragen der Datenbank würde den Hauptthread stoppen, also würde es anscheinend genauso lange dauern wie synchroner Code? Bitte korrigieren Sie mich, wenn ich etwas übersehe.
Marco Topolnik
runBlocking ist der Weg, synchronen und asynchronen Code zu überbrücken
Ich stoße immer wieder auf diesen Satz und er ist sehr irreführend.
runBlocking ist fast nie ein Werkzeug, das Sie in der Produktion verwenden. Es macht die asynchrone, nicht blockierende Natur von Coroutinen rückgängig. Sie können es verwenden, wenn Sie bereits Coroutine-basierten Code haben, den Sie in einem Kontext verwenden möchten, in dem Coroutinen keinen Wert bieten: beim Blockieren von Aufrufen. Eine typische Verwendung ist das JUnit-Testen, bei dem die Testmethode einfach warten muss, bis die Coroutine abgeschlossen ist.
Sie können es auch verwenden, während Sie mit Koroutinen in Ihrem herumspielen main Methode.
Der Missbrauch von runBlocking ist so weit verbreitet, dass das Kotlin-Team tatsächlich versuchte, eine Fail-Fast-Prüfung hinzuzufügen, die Ihren Code sofort zum Absturz bringen würde, wenn Sie ihn im UI-Thread aufrufen. Als sie dies taten, war bereits so viel Code kaputt, dass sie ihn entfernen mussten.
Der Missbrauch von runBlocking ist so weit verbreitet, dass das Kotlin-Team Änderungen mit Fail-Fast rückgängig machen musste 🙂
– qwwdfsad
14. September 2018 um 15:35 Uhr
Diese Erklärung von Revert Fail-Fast ist eigentlich irreführend, es ist nicht nur das Problem einer Fehlbedienung (die definitiv existiert), das Problem war, dass es gültige Anwendungsfälle gibt, wie z. B. Anwendungsstart, Ressourcenbereinigung usw. Sie können eine Diskussion darüber in Coroutinen sehen Issue-Tracker, Lösung mit zusätzlichem Parameter “ja, block, ich weiß was ich tue, bitte nicht abstürzen” wäre imo nicht besser. Ein weiteres Problem war, dass es beim Release-Code fehlschlug, aber nicht beim Debuggen, was eine falsche Strategie ist, die Fehler verursacht, nicht verhindert
– Vergolder
10. Mai 2019 um 15:42 Uhr
Was sollte anstelle von runBlocking verwendet werden, um Coroutinen ohne Blockierung aufzurufen?
– Trevor
14. November 2019 um 21:56 Uhr
@ Trevor Du solltest launch eine Koroutine.
– Marko Topolnik
15. November 2019 um 7:15 Uhr
@ user3410835 runBlocking gibt das Ergebnis der von ihr gestarteten Coroutine zurück, was bedeutet, dass sie warten muss, bis sie abgeschlossen ist.
– Marko Topolnik
24. Mai 2020 um 17:13 Uhr
Roland
Eigentlich benutzt du runBlocking um suspendierende Funktionen in “blockierendem” Code aufzurufen, die sonst dort nicht aufrufbar wären, oder mit anderen Worten: Sie verwenden es zum Aufrufen suspend Funktionen außerhalb des Coroutine-Kontexts (in Ihrem Beispiel der Block, an den übergeben wird async ist der suspend Funktion). Außerdem (offensichtlicher, wie der Name schon sagt) ist der Anruf dann ein blockierender Anruf. In Ihrem Beispiel wird es also so ausgeführt, als ob es so etwas nicht gäbe async an Ort und Stelle. Es wartet (blockt unterbrechbar) bis alles innerhalb der runBlocking-Block ist fertig.
Nehmen Sie zum Beispiel eine Funktion in Ihrer Bibliothek wie folgt an:
suspend fun demo() : Any = TODO()
Diese Methode wäre z. B. nicht aufrufbar main. Für einen solchen Fall verwenden Sie runBlocking dann zB:
fun main(args: Array<String>) {
// demo() // this alone wouldn't compile... Error:() Kotlin: Suspend function 'demo' should be called only from a coroutine or another suspend function
// whereas the following works as intended:
runBlocking {
demo()
} // it also waits until demo()-call is finished which wouldn't happen if you use launch
}
Apropos Performance-Gewinn: Tatsächlich ist Ihre Anwendung möglicherweise eher reaktionsschneller als performanter (manchmal auch performanter, z. B. wenn Sie mehrere parallele Aktionen anstelle mehrerer sequentieller Aktionen haben). In Ihrem Beispiel blockieren Sie jedoch bereits, wenn Sie die Variable zuweisen, sodass ich sagen würde, dass Ihre App noch nicht reaktionsschneller wird. Möglicherweise möchten Sie Ihre Abfrage lieber asynchron aufrufen und dann die Benutzeroberfläche aktualisieren, sobald die Antwort verfügbar ist. Also lässt du es im Grunde einfach weg runBlocking und verwenden Sie lieber so etwas wie launch. Das könnte Sie auch interessieren Leitfaden zur UI-Programmierung mit Coroutinen.
könnten Sie bitte erklären, was “Suspend-Funktionen außerhalb des Coroutine-Kontexts aufruft”. bedeuten? Du meinst, dass Suspend außerhalb des ist runBlocking Kontext? Ich stimme zu, aber die runBlocking würde erst zurückkehren, nachdem die Suspend-Funktion zurückkehrt, daher gibt es in meiner Option keinen Geschwindigkeitsgewinn. Der einzige Unterschied besteht darin, dass die Abfrage in einem anderen Thread ausgeführt wird
– Angelina
14. September 2018 um 12:02 Uhr
Beispiel hinzugefügt was ich meine … wenn es nicht klar ist, einfach fragen
– Roland
14. September 2018 um 12:06 Uhr
Beachten Sie auch, dass Sie in Fällen, in denen Sie tatsächlich asynchron laufen möchten, nicht verwenden runBlocking…
– Roland
14. September 2018 um 12:07 Uhr
meine Antwort ein wenig aktualisiert … auch in Bezug auf den Leistungsgewinn … beachten Sie, dass Ihr aktueller Code eigentlich fast derselbe ist wie eine synchrone Variante ohne Coroutinen … versuchen Sie es einfach wegzulassen runBlocking und verwenden launch und Sie erleben im Grunde den asynchronen Aufruf 😉
– Roland
14. September 2018 um 12:36 Uhr
Danke, dass dies einfacher zu verstehen ist als die Dokumentation