Estoy leyendo Kotlin Coroutine y sé que se basa en la suspendfunción. ¿Pero qué suspendsignifica?
¿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 awaitestá llamado computation, por lo que podría ser asyncque 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 suspendla asynccorrutina externa o suspendla computationcorrutina interna ?
Hace suspendmedia que mientras externa asynccorrutina está a la espera ( await) para el interior computationcorrutina a fin, él (el exterior asynccorrutina) Idles (de ahí el nombre de suspensión) y los retornos de rosca a la agrupación de hebras, y cuando el niño computationacabados 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 funse puede pausar, pero ¿cómo exactamente?Para entender qué significa exactamente suspender una corrutina, le sugiero que lea este código:
El
Unconfineddespachador de corrutinas elimina la magia del despacho de corrutinas y nos permite enfocarnos directamente en corrutinas desnudas.El código dentro del
launchbloque comienza a ejecutarse de inmediato en el hilo actual, como parte de lalaunchllamada. Lo que sucede es lo siguiente:val a = a()b(), alcanzandosuspendCoroutine.b()ejecuta el bloque pasadosuspendCoroutiney luego devuelve unCOROUTINE_SUSPENDEDvalor 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.launchbloque hace lo mismo y el control ahora vuelve a la línea después de lalaunchinvocación:10.downTo(0)...Tenga en cuenta que, en este punto, tiene el mismo efecto que si el código dentro del
launchbloque y sufun maincódigo se estuvieran ejecutando al mismo tiempo. Simplemente sucede que todo esto está sucediendo en un solo hilo nativo, por lo que ellaunchbloque está "suspendido".Ahora, dentro del
forEachcódigo de bucle, el programa lee elcontinuationqueb()escribió la función yresumesel valor de10.resume()se implementa de tal manera que será como si lasuspendCoroutinellamada 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 asignaiy 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 laprintlninstrucció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
launchinvocació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
continuationobjeto 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
asyncinicia una corrutina. Cuando llamacomputation(), el interiorasyncinicia 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
asynccomienzo 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 externoasynchasta que llegaawait().await()es un "punto de suspensión", porqueawaites 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
asyncfunciones de JS estén marcadas de esta manera y, sin embargo, devuelvan una Promesa.Descubrí que la mejor manera de entenderlo
suspendes hacer una analogía entrethispalabra clave ycoroutineContextpropiedad.Las funciones de Kotlin se pueden declarar como locales o globales. Las funciones locales mágicamente tienen acceso a las
thispalabras clave, mientras que las globales no.Las funciones de Kotlin se pueden declarar
suspendo bloquear.suspendlas funciones mágicamente tienen acceso a lacoroutineContextpropiedad mientras que las funciones de bloqueo no.La cuestión es que la
coroutineContextpropiedad 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,coroutineContextes 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
thishace la palabra clave para las funciones locales es lo quecoroutineContexthace la propiedad para lassuspendfunciones: da acceso al contexto actual de ejecución.Por lo tanto, debe
suspendobtener acceso a lacoroutineContextpropiedad: 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
suspendfunció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
onResumeAlmacene una variable llamada
continuationy 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.
suspendfunciónrunBlocking { }inicia una Coroutine en forma de bloqueo. Es similar a cómo estábamos bloqueando los hilos normales conThreadclase 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.workerhilo.workersubproceso 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
scopesque especifican la vida útil de la rutina. Si especificamosGlobalScope, la corrutina funcionará hasta que finalice la vida útil de la aplicación.Esto produce:
asyncyawaitayudarían.2funciones de suspensión myMethod () y myMethod2 ().myMethod2()debe ejecutarse solo después de la finalización completa demyMethod()ORmyMethod2()depende del resultado demyMethod(), podemos usarasyncyawaitasyncinicia 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().asyncdevuelve una instancia deDeffered<T>.TseríaUnitpor defecto. Cuando tengamos que esperar a que seasynccomplete 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 queinnerAsyncse complete. Podemos observar lo mismo en la salida. ElinnerAsyncse completó primero, que llamamyMethod(). Y luegoasyncinnerAsync2comienza a continuación , que llamamyMethod2()Esto produce:
fuente