support - libro kotlin español pdf
Diferencia entre hilo y coroutine en Kotlin. (2)
¿Existe alguna implementación de lenguaje específica en Kotlin, que la diferencie de la implementación de otros lenguajes de coroutines?
- ¿Qué significa que la coroutina es como hilo ligero?
- ¿Cuál es la diferencia?
- ¿Las corutinas de Kotlin se ejecutan en paralelo / concurrentemente?
- Incluso en un sistema de múltiples núcleos, solo hay una corrutina en ejecución en un momento dado (¿es correcto?)
Aquí estoy empezando 100000 coroutines, ¿qué pasa detrás de este código?
for(i in 0..100000){
async(CommonPool){
//run long running operations
}
}
¿Qué significa que la coroutina es como hilo ligero?
Coroutine, como un hilo, representa una secuencia de acciones que se ejecutan simultáneamente con otros coroutines (hilos).
¿Cuál es la diferencia?
Un hilo está directamente vinculado al hilo nativo en el sistema operativo correspondiente (sistema operativo) y consume una cantidad considerable de recursos. En particular, consume mucha memoria por su pila. Es por eso que no puedes crear solo 100k hilos. Es probable que se quede sin memoria. El cambio entre subprocesos implica al distribuidor del kernel del sistema operativo y es una operación bastante costosa en términos de ciclos de CPU consumidos.
Una coroutine, por otro lado, es puramente una abstracción del lenguaje a nivel de usuario. No vincula ningún recurso nativo y, en el caso más simple, utiliza solo un objeto relativamente pequeño en el montón de JVM. Por eso es fácil crear 100k coroutines. Cambiar entre coroutines no implica en absoluto el núcleo del sistema operativo. Puede ser tan barato como invocar una función regular.
¿Las corutinas de Kotlin se ejecutan en paralelo / concurrentemente? Incluso en un sistema de múltiples núcleos, solo hay una corrutina en ejecución en un momento dado (¿es correcto?)
Una coroutine puede estar en ejecución o suspendida. Un coroutine suspendido no está asociado a ningún subproceso en particular, pero un coroutine en ejecución se ejecuta en algún subproceso (el uso de un subproceso es la única forma de ejecutar algo dentro de un proceso del sistema operativo). El hecho de que todos los corutines diferentes se ejecuten en el mismo subproceso (por lo tanto, puede usar solo una sola CPU en un sistema multinúcleo) o en diferentes subprocesos (y, por lo tanto, puede usar múltiples CPU) está en manos de un programador que usa coroutines.
En Kotlin, el envío de coroutines se controla a través del contexto de coroutine . Puedes leer más sobre entonces en la Guía de kotlinx.coroutines
Aquí estoy empezando 100000 coroutines, ¿qué pasa detrás de este código?
Suponiendo que está utilizando la función de launch
y el contexto de kotlinx.coroutines
proyecto kotlinx.coroutines
(que es de código abierto), puede examinar su código fuente aquí:
-
launch
se define aquí https://github.com/Kotlin/kotlinx.coroutines/blob/master/core/kotlinx-coroutines-core/src/main/kotlin/kotlinx/coroutines/experimental/Builders.kt -
CommonPool
se define aquí https://github.com/Kotlin/kotlinx.coroutines/blob/master/core/kotlinx-coroutines-core/src/main/kotlin/kotlinx/coroutines/experimental/CommonPool.kt
El launch
simplemente crea una nueva coroutine, mientras que CommonPool
distribuye las coroutines a un ForkJoinPool.commonPool()
que usa múltiples subprocesos y, por lo tanto, se ejecuta en múltiples CPU en este ejemplo.
El código que sigue a la invocación de launch
en {...}
se llama un lambda de suspensión . ¿Qué es y cómo se suspenden las lambdas y las funciones implementadas (compiladas), así como las funciones y clases de la biblioteca estándar como startCoroutines
, suspendCoroutine
y CoroutineContext
se explican en el documento de diseño de Kotlin Coroutines correspondiente ?
Ya que solo utilicé coroutines en JVM, hablaré sobre el backend de JVM, también hay Kotlin Native y Kotlin JavaScript, pero esto no funciona para Kotlin.
Así que vamos a empezar con la comparación de Koutlin coroutines a otros idiomas. Básicamente debes saber que hay dos tipos de Coroutines: sin pila y sin pila. Kotlin implementa coroutines sin pila: significa que la coroutine no tiene su propia pila, y que limita un poco lo que puede hacer la coroutine. Puedes leer una buena explicación here .
Ejemplos:
- Sin apilamiento: C #, Scala, Kotlin
- Stackful: Quasar, Javaflow
¿Qué significa que la coroutina es como hilo ligero?
Significa que Coroutine en Kotlin no tiene pila propia, no se mapea en un hilo nativo, no requiere un cambio de contexto en el procesador.
¿Cuál es la diferencia?
Hilo - multitarea de forma preventiva. (por lo usually ). Coroutine - cooperativamente multitarea.
Hilo - gestionado por el sistema operativo (normalmente). Coroutine - gestionado por el usuario.
¿Las corutinas de Kotlin se ejecutan en paralelo / concurrentemente?
Depende, puede ejecutar cada coroutine en su propio subproceso o puede ejecutar todas las coroutines en un subproceso o en un grupo de subprocesos fijo.
Más acerca de cómo coroutines se ejecuta here .
Incluso en un sistema de múltiples núcleos, solo hay una corrutina en ejecución en un momento dado (¿es correcto?)
No, ver respuesta anterior.
Aquí estoy empezando 100000 coroutines, ¿qué pasa detrás de este código?
En realidad depende. Pero supongamos que escribes el siguiente código:
fun main(args: Array<String>) {
for (i in 0..100000) {
async(CommonPool) {
delay(1000)
}
}
}
Este código se ejecuta al instante.
Porque hay que esperar resultados de llamada async
.
Así que vamos a arreglar esto:
fun main(args: Array<String>) = runBlocking {
for (i in 0..100000) {
val job = async(CommonPool) {
delay(1)
println(i)
}
job.join()
}
}
Cuando ejecute este programa, kotlin creará 2 * 100000 instancias de Continuation
, que tomarán unas docenas de Mb de RAM, y en la consola verá números del 1 al 100000.
Así que vamos a reescribir este código de esta manera:
fun main(args: Array<String>) = runBlocking {
val job = async(CommonPool) {
for (i in 0..100000) {
delay(1)
println(i)
}
}
job.join()
}
¿Qué logramos ahora? Ahora creamos solo 100001 instancias de Continuation
, y esto es mucho mejor.
Cada Continuación creada se enviará y ejecutará en CommonPool (que es una instancia estática de ForkJoinPool).