Estoy leyendo Kotlin Coroutine y sé que se basa en la suspend
función. ¿Pero qué suspend
significa?
¿Corutina o función se suspende?
De https://kotlinlang.org/docs/reference/coroutines.html
Básicamente, las corrutinas son cálculos que se pueden suspender sin bloquear un hilo.
Escuché que la gente suele decir "suspender la función". Pero creo que es la corrutina la que se suspende porque está esperando a que finalice la función. "suspender" normalmente significa "cesar la operación", en este caso la corrutina está inactiva.
🤔 ¿Deberíamos decir que la corrutina está suspendida?
¿Qué corrutina se suspende?
De 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 invocable desde dentro de un bloque async {}) que suspende una corrutina 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 "que suspende una corrutina hasta que se realiza algún cálculo", pero la corrutina es como un hilo ligero. Entonces, si se suspende la corrutina, ¿cómo se puede realizar el cálculo?
Vemos que await
está llamado computation
, por lo que podría ser async
que regrese Deferred
, lo que significa que puede iniciar otra corrutina
fun computation(): Deferred<Boolean> {
return async {
true
}
}
🤔 La cita dice que suspende una corrutina . ¿Significa suspend
la async
corrutina externa o suspend
la computation
corrutina interna ?
Hace suspend
media que mientras externa async
corrutina está a la espera ( await
) para el interior computation
corrutina a fin, él (el exterior async
corrutina) Idles (de ahí el nombre de suspensión) y los retornos de rosca a la agrupación de hebras, y cuando el niño computation
acabados co-rutina, él (el exteriorasync
corrutina ) se despierta, toma otro hilo de la piscina y continúa?
La razón por la que menciono el hilo es por https://kotlinlang.org/docs/tutorials/coroutines-basic-jvm.html
El hilo se devuelve al grupo mientras la corrutina está esperando, y cuando finaliza la espera, la corrutina se reanuda en un hilo libre en el grupo.
fuente
suspend fun
se puede pausar, pero ¿cómo exactamente?Para entender qué significa exactamente suspender una corrutina, le sugiero que lea este código:
El
Unconfined
despachador de corrutinas elimina la magia del despacho de corrutinas y nos permite enfocarnos directamente en corrutinas desnudas.El código dentro del
launch
bloque comienza a ejecutarse de inmediato en el hilo actual, como parte de lalaunch
llamada. Lo que sucede es lo siguiente:val a = a()
b()
, alcanzandosuspendCoroutine
.b()
ejecuta el bloque pasadosuspendCoroutine
y luego devuelve unCOROUTINE_SUSPENDED
valor especial . 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.a()
, al ver este valor de retorno, también lo devuelve.launch
bloque hace lo mismo y el control ahora vuelve a la línea después de lalaunch
invocación:10.downTo(0)...
Tenga en cuenta que, en este punto, tiene el mismo efecto que si el código dentro del
launch
bloque y sufun main
código se estuvieran ejecutando al mismo tiempo. Simplemente sucede que todo esto está sucediendo en un solo hilo nativo, por lo que ellaunch
bloque está "suspendido".Ahora, dentro del
forEach
código de bucle, el programa lee elcontinuation
queb()
escribió la función yresumes
el valor de10
.resume()
se implementa de tal manera que será como si lasuspendCoroutine
llamada regresara con el valor que le pasaste. Así que de repente te encuentras en medio de la ejecuciónb()
. El valor al que pasóresume()
se asignai
y se compara0
. Si no es cero, elwhile (true)
bucle continúa hacia adentrob()
, alcanzando nuevamentesuspendCoroutine
, momento en el que suresume()
llamada regresa, y ahora pasa por otro paso de bucle hacia adentroforEach()
. Esto continúa hasta que finalmente reanude con0
, luego laprintln
instrucción se ejecuta y el programa se completa.El análisis anterior debería darle la importante intuición de que "suspender una corrutina" significa devolver el control a la
launch
invocación más interna (o, más generalmente, al constructor de corrutinas ). Si una corrutina vuelve a suspenderse después de reanudarse, laresume()
llamada finaliza y el control vuelve al llamador deresume()
.La presencia de un despachador de rutina 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 rutina también administra el
continuation
objeto para que pueda reanudarlo cuando el valor de retorno esté disponible.fuente
En primer lugar, la mejor fuente para entender esta OMI es la charla "Deep Dive into Coroutines" de Roman Elizarov.
Llamar a una suspensión de ing función de suspensión de s la co-rutina, es decir, el hilo actual puede comenzar a ejecutar otra corrutina. Entonces, se dice que la corrutina está suspendida en lugar de la función.
De hecho, los sitios de llamada de funciones de suspensión se denominan "puntos de suspensión" por este motivo.
Veamos su código y analicemos lo que sucede:
El exterior
async
inicia una corrutina. Cuando llamacomputation()
, el interiorasync
inicia una segunda corrutina. Entonces, la llamada aawait()
suspende la ejecución de la corrutina externaasync
, hasta que finaliza la ejecución de la corrutina internaasync
.Incluso puedes ver eso con un solo hilo: el hilo ejecutará el
async
comienzo del exterior , luego llamarácomputation()
y llegará al interiorasync
. En este punto, se omite el cuerpo del async interno y el hilo continúa ejecutando el externoasync
hasta que llegaawait()
.await()
es un "punto de suspensión", porqueawait
es una función de suspensión. Esto significa que la corrutina externa está suspendida y, por lo tanto, el hilo comienza a ejecutar la interna. Cuando está hecho, vuelve a ejecutar el final del exteriorasync
.Sí, precisamente.
La forma en que esto se logra realmente es convirtiendo cada función de suspensión en una máquina de estado, donde cada "estado" corresponde a un punto de suspensión dentro de esta función de suspensión. Debajo del capó, la función se puede llamar varias veces, con la información sobre desde qué punto de suspensión debe comenzar a ejecutarse (realmente debería ver el video que vinculé para obtener más información al respecto).
fuente
async
funciones de JS estén marcadas de esta manera y, sin embargo, devuelvan una Promesa.Descubrí que la mejor manera de entenderlo
suspend
es hacer una analogía entrethis
palabra clave ycoroutineContext
propiedad.Las funciones de Kotlin se pueden declarar como locales o globales. Las funciones locales mágicamente tienen acceso a las
this
palabras clave, mientras que las globales no.Las funciones de Kotlin se pueden declarar
suspend
o bloquear.suspend
las funciones mágicamente tienen acceso a lacoroutineContext
propiedad mientras que las funciones de bloqueo no.La cuestión es que la
coroutineContext
propiedad se declara como una propiedad "normal" en Kotlin stdlib pero esta declaración es solo un código auxiliar para propósitos de documentación / navegación. De hecho,coroutineContext
es una propiedad intrínseca incorporada que significa que la magia del compilador bajo el capó es consciente de esta propiedad, así como es consciente de las palabras clave del lenguaje.Lo que
this
hace la palabra clave para las funciones locales es lo quecoroutineContext
hace la propiedad para lassuspend
funciones: da acceso al contexto actual de ejecución.Por lo tanto, debe
suspend
obtener acceso a lacoroutineContext
propiedad: la instancia del contexto de rutina ejecutado actualmentefuente
Quería darles un ejemplo sencillo del concepto de continuación. Esto es lo que hace una función de suspensión, se puede congelar / suspender y luego continúa / se reanuda. Deja de pensar en la corrutina en términos de hilos y semáforo. Piense en ello en términos de continuación e incluso ganchos de devolución de llamada.
Para que quede claro, una corrutina se puede pausar usando una
suspend
función. investiguemos esto:En Android podríamos hacer esto por ejemplo:
El código anterior imprime lo siguiente:
imagínelo funcionando así:
Por lo tanto, la función actual desde la que inició no se detiene, solo una corrutina se suspendería mientras continúa. El hilo no se pausa al ejecutar una función de suspensión.
Creo que este sitio puede ayudarte a aclarar las cosas y es mi referencia.
Hagamos algo interesante y congelemos nuestra función de suspensión en medio de una iteración. Lo reanudaremos más tarde en
onResume
Almacene una variable llamada
continuation
y la cargaremos con el objeto de continuación de corrutinas por nosotros:Ahora, volvamos a nuestra función suspendida y congelemos en medio de la iteración:
Luego, en otro lugar como onResume (por ejemplo):
Y el ciclo continuará. Es bueno saber que podemos congelar una función de suspensión en cualquier momento y reanudarla después de que haya pasado un tiempo. También puedes buscar canales
fuente
Como ya hay muchas buenas respuestas, me gustaría publicar un ejemplo más simple para otros.
suspend
funciónrunBlocking { }
inicia una Coroutine en forma de bloqueo. Es similar a cómo estábamos bloqueando los hilos normales conThread
clase y notificando los hilos bloqueados después de ciertos eventos.runBlocking { }
no bloquear la corriente de la ejecución de hilo, hasta que el co-rutina (cuerpo entre{}
) se completóEsto produce:
launch { }
inicia una corrutina al mismo tiempo.worker
hilo.worker
subproceso y el subproceso externo (desde el que llamamoslaunch { }
) se ejecutan simultáneamente. Internamente, JVM puede realizar subprocesos preventivosCuando necesitamos que varias tareas se ejecuten en paralelo, podemos usar esto. Hay los
scopes
que especifican la vida útil de la rutina. Si especificamosGlobalScope
, la corrutina funcionará hasta que finalice la vida útil de la aplicación.Esto produce:
async
yawait
ayudarían.2
funciones de suspensión myMethod () y myMethod2 ().myMethod2()
debe ejecutarse solo después de la finalización completa demyMethod()
ORmyMethod2()
depende del resultado demyMethod()
, podemos usarasync
yawait
async
inicia una corrutina en paralelo similar alaunch
. Pero proporciona una forma de esperar una corrutina antes de iniciar otra corrutina en paralelo.De esa manera es
await()
.async
devuelve una instancia deDeffered<T>
.T
seríaUnit
por defecto. Cuando tengamos que esperar a que seasync
complete alguno , debemos llamar.await()
a unaDeffered<T>
instancia de esoasync
. Como en el siguiente ejemplo, llamamos, loinnerAsync.await()
que implica que la ejecución se suspendería hasta queinnerAsync
se complete. Podemos observar lo mismo en la salida. ElinnerAsync
se completó primero, que llamamyMethod()
. Y luegoasync
innerAsync2
comienza a continuación , que llamamyMethod2()
Esto produce:
fuente