rutinas hilos corutinas coroutines async-await kotlin coroutine suspend

async-await - corutinas - hilos en kotlin



¿Qué significa la función suspender en Kotlin Coroutine? (3)

Estoy leyendo Kotlin Coroutine y sé que se basa en la función de suspend . ¿Pero qué significa suspend ?

¿Coroutine o función se suspende?

Desde https://kotlinlang.org/docs/reference/coroutines.html

Básicamente, las corrutinas son cálculos que pueden suspenderse sin bloquear un hilo.

He oído a la gente decir a menudo "suspender la función". Pero creo que es la coroutina la que se suspende porque está esperando a que termine la función. "suspender" por lo general significa "cesar la operación", en este caso el coroutine está inactivo.

🤔 ¿Debemos decir que la coroutina está suspendida?

¿Qué coroutine se suspende?

Desde https://kotlinlang.org/docs/reference/coroutines.html

Para continuar con la analogía, await () puede ser una función de suspensión (por lo tanto, también se puede llamar desde dentro de un bloque asíncrono {}) que suspende una rutina hasta que se realiza algún cálculo y devuelve su resultado:

async { // Here I call it the outer async coroutine ... // Here I call computation the inner coroutine val result = computation.await() ... }

Dice "eso suspende una coroutine hasta que se realiza algún cálculo", pero la coroutine es como un hilo liviano. Entonces, si se suspende la coroutina, ¿cómo se realiza el cálculo?

Vemos que se llama await en el computation , por lo que podría ser async que devuelve Deferred , lo que significa que puede iniciar otra rutina.

fun computation(): Deferred<Boolean> { return async { true } }

🤔 La cita dice que suspende una coroutina . ¿Significa suspend la rutina externa async o suspend la computation interna?

suspend significa que, mientras la coroutina externa async está esperando ( await ) a que finalice la computation interna para computation , ésta (la coroutina async externa) está inactiva (de ahí el nombre de suspensión) y devuelve el hilo a la agrupación de hilos, y cuando finaliza la computation secundaria coroutine , ¿(la coroutina async externa) se despierta, toma otro hilo de la piscina y continúa?

La razón por la que menciono el hilo es debido a https://kotlinlang.org/docs/tutorials/coroutines-basic-jvm.html

El hilo se devuelve a la piscina mientras el coroutine está esperando, y cuando se hace la espera, el coroutine se reanuda en un hilo libre en el pool


¿Coroutine o función se suspende?

Llamar a una función de suspensión suspende la rutina, lo que significa que el hilo actual comienza a ejecutar otra conexión. Entonces, se dice que la coroutina está suspendida.

Pero técnicamente, tanto la función como la rutina se detienen, porque se suspende la ejecución del código de su función, en el contexto de la línea actual. Esto se debe a que su función no será ejecutada por otra persona en ese momento. Pero estamos dividiendo los pelos aquí.

¿Qué coroutine se suspende?

La async exterior inicia una coroutina. Cuando llama a computation() , el async interno inicia una segunda rutina. Luego, la llamada a await() suspende la ejecución de la coroutine async externa, hasta que finaliza la ejecución de la coroutine async interna .

Incluso puedes ver eso con un solo hilo: el hilo ejecutará el comienzo del async externo, luego llamará computation() y alcanzará el async interno. En este punto, el cuerpo del async interno se omite y el hilo continúa ejecutando el async externo hasta que llega a la await() . await() es un "punto de suspensión", porque await es una función de suspensión. Esto significa que la coroutina externa está suspendida, y así el hilo comienza a ejecutar la interna. Cuando se hace, vuelve a ejecutar el final de la async externa.

Suspender significa que, mientras la coroutina externa asíncrona está esperando (esperando) a que finalice la computación interna para computación, ésta (la coroutina asíncrona externa) está inactiva (de ahí el nombre de suspensión) y devuelve el hilo a la agrupación de hilos, y cuando finaliza la computación secundaria coroutine , ¿(la coroutina asíncrona externa) se despierta, toma otro hilo de la piscina y continúa?

Sí, precisamente.


Como herramienta de aprendizaje, le sugiero que vaya a través de este código, que expone el mecanismo básico que subyace en todas las construcciones de conveniencia, como async :

import kotlinx.coroutines.experimental.Unconfined import kotlinx.coroutines.experimental.launch import kotlin.coroutines.experimental.Continuation import kotlin.coroutines.experimental.suspendCoroutine var continuation: Continuation<Int>? = null fun main(args: Array<String>) { launch(Unconfined) { val a = a() println("Result is $a") } 10.downTo(0).forEach { continuation!!.resume(it) } } suspend fun a(): Int { return b() } suspend fun b(): Int { while (true) { val i = suspendCoroutine<Int> { cont -> continuation = cont } if (i == 0) { return 0 } } }

El despachador de Unconfined Unconfined básicamente elimina la magia del despacho de coroutine: el código dentro del bloque de launch simplemente comienza a ejecutarse como parte de la llamada de launch . Lo que pasa es lo siguiente:

  1. Evaluar val a = a()
  2. Esto encadena a b() , llegando a suspendCoroutine .
  3. La función b() ejecuta el bloque pasado a suspendCoroutine y luego devuelve un valor especial COROUTINE_SUSPENDED . Este valor no es observable a través del modelo de programación de Kotlin, pero eso es lo que hace el método Java compilado.
  4. La función a() , al ver este valor de retorno, también lo devuelve.
  5. El bloque de launch hace lo mismo y el control ahora vuelve a la línea después de la invocación de launch : 10.downTo(0)...

Tenga en cuenta que, en este punto, tendrá el mismo efecto que si el código dentro del bloque de launch y su fun main código fun main se ejecutaran simultáneamente. Simplemente sucede que todo esto está sucediendo en un solo hilo nativo, por lo que el bloque de launch está "suspendido".

Ahora, dentro del código de bucle forEach , el programa lee la continuation que escribió la función b() y la resumes con el valor de 10 . resume() se implementa de tal manera que será como si la llamada suspendCoroutine regresara con el valor que usted suspendCoroutine . Así que de repente se encuentra en medio de ejecutar b() . El valor que pasó para resume() se asigna a i y se compara con 0 . Si no es cero, el bucle while (true) continúa dentro de b() , nuevamente llegando a suspendCoroutine , momento en el cual su llamada de resume() regresa, y ahora pasa por otro paso de bucle en forEach() . Esto continúa hasta que finalmente se reanuda con 0 , luego se println instrucción println y el programa se completa.

El análisis anterior debería darle la importante intuición de que "suspender una coroutine" significa devolver el control a la invocación de launch más interna (o, más generalmente, al constructor de coroutine ). Si un coroutine se suspende nuevamente después de reanudar, la llamada resume() finaliza y el control regresa al interlocutor de resume() .

La presencia de un despachador de coroutine hace que este razonamiento sea menos claro porque la mayoría de ellos envían inmediatamente su código a otro hilo. En ese caso, la historia anterior ocurre en ese otro hilo, y el despachador de Coroutine también administra el objeto de continuation para que pueda reanudarlo cuando el valor de retorno esté disponible.


Las funciones de suspensión están en el centro de todo lo relacionado con las rutinas. Una función de suspensión es simplemente una función que se puede pausar y reanudar posteriormente. Pueden ejecutar una operación de larga duración y esperar a que se complete sin bloqueo.

La sintaxis de una función de suspensión es similar a la de una función normal, excepto por la adición de la palabra clave de suspensión. Puede tomar un parámetro y tener un tipo de retorno. Sin embargo, las funciones de suspensión solo pueden ser invocadas por otra función de suspensión o dentro de una rutina.

suspend fun backgroundTask(param: Int): Int { // long running operation }

Bajo el capó, las funciones de suspensión son convertidas por el compilador a otra función sin la palabra clave de suspensión, que toma un parámetro de adición de tipo Continuación. La función anterior, por ejemplo, será convertida por el compilador a esto:

fun backgroundTask(param: Int, callback: Continuation<Int>): Int { // long running operation }

La continuación es una interfaz que contiene dos funciones que se invocan para reanudar la rutina con un valor de retorno o con una excepción si se produjo un error mientras se suspendía la función.

interface Continuation<in T> { val context: CoroutineContext fun resume(value: T) fun resumeWithException(exception: Throwable) }