Saltar al contenido

Corutinas: runBlocking vs coroutineScope

Solución:

No entiendo en qué se diferencian coroutineScope y runBlocking aquí. coroutineScope parece estar bloqueado, ya que solo llega a la última línea cuando termina.

Desde la perspectiva del código en el bloque, su comprensión es correcta. La diferencia entre runBlocking y coroutineScope sucede en un nivel inferior: ¿qué le sucede al hilo mientras la corrutina está bloqueada?

  • runBlocking no es un suspend fun. El hilo que lo llamó permanece dentro de él hasta que se completa la corrutina.

  • coroutineScope es un suspend fun. Si su corrutina se suspende, el coroutineScope la función también se suspende. Esto permite que la función de nivel superior, no suspendido función que creó la corrutina, para continuar ejecutándose en el mismo hilo. El hilo ha “escapado” del coroutineScope bloque y está listo para hacer algún otro trabajo.

En su ejemplo específico: cuando su coroutineScope suspende, el control vuelve al código de implementación dentro runBlocking. Este código es un bucle de eventos que impulsa todas las corrutinas que inició dentro de él. En su caso, habrá algunas corrutinas programadas para ejecutarse después de un retraso. Cuando llegue el momento, se reanudará la corrutina correspondiente, que se ejecutará por un corto tiempo, se suspenderá y luego el control estará nuevamente dentro runBlocking.


Si bien lo anterior describe las similitudes conceptuales, también debería mostrarle que runBlocking es un completamente diferente herramienta de coroutineScope.

  • runBlocking es una construcción de bajo nivel, para ser utilizada solo en código marco o ejemplos independientes como el suyo. Convierte un hilo existente en un bucle de eventos y crea su corrutina con un Dispatcher que publica reanudación de corrutinas en la cola del bucle de eventos.

  • coroutineScope es una construcción orientada al usuario, que se utiliza para delinear los límites de una tarea que se descompone en paralelo dentro de ella. Lo usa para esperar cómodamente en todos los async el trabajo que ocurre dentro de él, obtenga el resultado final y maneje todas las fallas en un lugar central.

La respuesta elegida es buena pero no aborda algunos otros aspectos importantes del código de muestra que se proporcionó. Por ejemplo, el lanzamiento no es bloqueante y se supone que se ejecuta de inmediato. Eso simplemente no es cierto. El lanzamiento en sí regresa inmediatamente, PERO el código dentro del lanzamiento parece estar puesto en una cola y solo se ejecuta cuando se han completado otros lanzamientos que se colocaron previamente en la cola.

Aquí hay una pieza similar de código de muestra con todas las demoras eliminadas y un lanzamiento adicional incluido. Sin mirar el resultado a continuación, vea si puede predecir el orden en el que se imprimen los números. Lo más probable es que falles:

import kotlinx.coroutines.*

fun main() = runBlocking {
    launch { 
        println("1")
    }

    coroutineScope {
        launch {
            println("2")
        }

        println("3") 
    }

    coroutineScope {
        launch {
            println("4")
        }

        println("5")
    }

    launch { 
        println("6")
    }

    for (i in 7..100) {
        println(i.toString())
    }

    println("101")
}

El resultado es:

3
1
2
5
4
7
8
9
10
...
99
100
101
6

El hecho de que el número 6 se imprima en último lugar, incluso después de que se hayan ejecutado casi 100 println, indica que el código dentro del último lanzamiento nunca se ejecuta hasta que se completa todo el código no bloqueante después del lanzamiento. Pero eso tampoco es realmente cierto, porque si ese fuera el caso, el primer lanzamiento no debería haberse ejecutado hasta que se hayan completado los números 7 a 101. ¿Línea de fondo? La combinación de launch y coroutineScope es muy impredecible y debe evitarse si espera un cierto orden en la forma en que deben ejecutarse las cosas.

Para demostrar que el código dentro de los lanzamientos se coloca en una cola y SÓLO se ejecuta después de que TODO el código sin bloqueo se haya completado, ejecute esto (no se utilizan coroutineScopes):

import kotlinx.coroutines.*

fun main() = runBlocking {
    launch { 
        println("1")
    }

    launch { 
        println("2")
    }

    launch { 
        println("3")
    }

    for (i in 4..100) {
        println(i.toString())
    }

    println("101")
}

Este es el resultado que obtienes:

4
5
6
...
101
1
2
3

Agregar un CoroutineScope romperá este comportamiento. Hará que todo el código sin bloqueo que sigue a CoroutineScope no se ejecute hasta que TODO el código anterior a CoroutineScope se haya completado.

También debe tenerse en cuenta que en esta muestra de código, cada uno de los lanzamientos en la cola se ejecutan secuencialmente en el orden en que se agregan a la cola y cada lanzamiento solo se ejecutará DESPUÉS de que se ejecute el lanzamiento anterior. Esto puede hacer que parezca que todos los lanzamientos comparten un hilo común. Esto no es verdad. A cada uno de ellos se le asigna su propio hilo. Sin embargo, si algún código dentro de un lanzamiento llama a una función de suspensión, el siguiente lanzamiento en la cola se inicia inmediatamente mientras se lleva a cabo la función de suspensión. Para ser honesto, este es un comportamiento muy extraño. ¿Por qué no ejecutar todos los lanzamientos de la cola de forma asincrónica? Si bien no conozco los aspectos internos de lo que sucede en esta cola, supongo que cada lanzamiento en la cola no tiene su propio hilo, pero todos comparten un hilo común. Solo cuando se encuentra una función de suspensión parece que se crea un nuevo hilo para el próximo lanzamiento en la cola. Puede hacerse de esta manera para ahorrar recursos.

En resumen, la ejecución se realiza en este orden:

  1. El código dentro de un lanzamiento se coloca dentro de una cola y se ejecuta en el orden en que se agregan.
  2. El código sin bloqueo que sigue a un lanzamiento se ejecuta inmediatamente antes de que se ejecute cualquier cosa en la cola.
  3. Un CoroutineScope bloquea TODO el código que lo sigue, PERO ejecutará todas las corrutinas de inicio en la cola antes de reanudar el código que sigue a CoroutineScope.

runBlocking te permite bloquear el hilo principal.

coroutineScope es para que usted bloquee runBlocking.

¡Haz clic para puntuar esta entrada!
(Votos: 0 Promedio: 0)



Utiliza Nuestro Buscador

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *