Corrutinas de Kotlin `runBlocking`

5 minutos de lectura

Avatar de usuario de Angelina
Angelina

Estoy aprendiendo rutinas de Kotlin. he leido eso runBlocking es la forma de unir el código sincrónico y asincrónico. Pero, ¿cuál es la ganancia de rendimiento si el runBlocking detiene el hilo de la interfaz de usuario? Por ejemplo, necesito consultar una base de datos en Android:

    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()
        }
        return@async queryResult
    }
}

Consultar la base de datos detendría el hilo principal, por lo que parece que tomaría la misma cantidad de tiempo que el código síncrono. Por favor corrígeme si me estoy perdiendo algo.

Avatar de usuario de Marko Topolnik
Marko Topolnik

runBlocking es la forma de unir el código síncrono y asíncrono

Sigo topándome con esta frase y es muy engañosa.

runBlocking es casi nunca una herramienta que utiliza en la producción. Deshace la naturaleza asincrónica y no bloqueante de las rutinas. Puede usarlo si ya tiene algún código basado en rutinas que desea usar en un contexto donde las rutinas no brindan ningún valor: en el bloqueo de llamadas. Un uso típico es la prueba JUnit, donde el método de prueba simplemente debe esperar a que se complete la rutina.

También puedes usarlo mientras juegas con rutinas, dentro de tu main método.

el mal uso de runBlocking se ha generalizado tanto que el equipo de Kotlin en realidad intentó agregar una verificación rápida que bloquearía inmediatamente su código si lo llama en el subproceso de la interfaz de usuario. Cuando hicieron esto, ya estaba rompiendo tanto código que tuvieron que eliminarlo.

  • el mal uso de runBlocking se ha generalizado tanto que el equipo de Kotlin ha tenido que revertir el cambio con fail-fast 🙂

    – qwwdfsad

    14/09/2018 a las 15:35

  • Esta explicación de revertir fallas rápidamente es en realidad engañosa, no es solo un problema de un mal uso (que definitivamente existe), el problema fue que hay casos de uso válidos, como el inicio de la aplicación, la limpieza de recursos, etc. rastreador de problemas, solución con parámetro adicional “sí, bloque, sé lo que estoy haciendo, por favor no bloquee” no sería mejor en mi opinión. Un problema más fue que falló en el código de lanzamiento, pero no en la depuración, que es una estrategia incorrecta que causa errores, no los previene.

    – dorador

    10 mayo 2019 a las 15:42


  • ¿Qué se debe usar en lugar de runBlocking para llamar coroutines sin bloquear?

    – Trevor

    14 de noviembre de 2019 a las 21:56

  • @Trevor Deberías launch una rutina.

    – Marko Topolnik

    15 de noviembre de 2019 a las 7:15

  • @usuario3410835 runBlocking devuelve el resultado de la rutina que inicia, lo que significa que debe esperar a que se complete.

    – Marko Topolnik

    24 de mayo de 2020 a las 17:13

Avatar de usuario de Roland
roland

en realidad usas runBlocking para llamar a funciones de suspensión en código de “bloqueo” que de otro modo no se podría llamar allí o en otras palabras: lo usa para llamar suspend funciones fuera del contexto coroutine (en su ejemplo, el bloque pasó a async es el suspend función). Además (más obvio, como ya implica el propio nombre), la llamada es una llamada de bloqueo. Entonces, en su ejemplo, se ejecuta como si no hubiera algo como async en su lugar. espera (bloquea interrumpiblemente) hasta que todo dentro del runBlocking-el bloque está terminado.

Por ejemplo, suponga una función en su biblioteca de la siguiente manera:

suspend fun demo() : Any = TODO()

Este método no sería invocable desde, por ejemplo main. Para tal caso se utiliza runBlocking entonces, por ejemplo:

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
}

Con respecto a la ganancia de rendimiento: en realidad, su aplicación puede ser más receptiva en lugar de tener más rendimiento (a veces también más rendimiento, por ejemplo, si tiene varias acciones paralelas en lugar de varias secuenciales). En su ejemplo, sin embargo, ya bloquea cuando asigna la variable, por lo que diría que su aplicación aún no responde mejor. Es posible que desee llamar a su consulta de forma asincrónica y luego actualizar la interfaz de usuario tan pronto como la respuesta esté disponible. Así que básicamente simplemente omites runBlocking y más bien usar algo como launch. Usted también podría estar interesado en Guía de programación de interfaz de usuario con rutinas.

  • ¿Podría explicar qué significa “llamar a las funciones de suspensión fuera del contexto coroutine”. ¿significar? quiere decir que suspender está fuera del runBlocking ¿contexto? Estoy de acuerdo, pero el runBlocking regresaría solo después de que regrese la función de suspensión, por lo que no hay ganancia de velocidad en mi opción. La única diferencia es que la consulta se ejecuta en un hilo diferente

    – angelina

    14 de septiembre de 2018 a las 12:02


  • agregó una muestra de lo que quiero decir… si no está claro, solo pregunte

    – roland

    14 de septiembre de 2018 a las 12:06

  • tenga en cuenta también que para los casos en los que realmente desea ejecutar de forma asíncrona, no utiliza runBlocking

    – roland

    14 de septiembre de 2018 a las 12:07

  • actualicé un poco mi respuesta … también con respecto a la ganancia de rendimiento … tenga en cuenta que su código actual es casi el mismo que una variante síncrona sin rutinas … solo intente omitir runBlocking y use launch y básicamente experimentas la llamada asíncrona 😉

    – roland

    14 de septiembre de 2018 a las 12:36

  • Gracias por esto más fácil de entender que la documentación.

    – dave o grady

    26 de diciembre de 2019 a las 23:51

¿Ha sido útil esta solución?