architecture multithreading system coroutine

architecture - ¿Diferencia entre una "corrutina" y un "hilo"?



multithreading system (5)

¿Cuáles son las diferencias entre una "coroutine" y un "hilo"?


Aproximadamente 7 años tarde, pero las respuestas aquí carecen de contexto en co-rutinas vs hilos. ¿Por qué las coroutines reciben tanta atención últimamente, y cuándo las usaría en comparación con los hilos ?

En primer lugar, si las corrutinas se ejecutan al mismo tiempo (nunca en paralelo ), ¿por qué alguien las prefiere por encima de los hilos?

La respuesta es que las corutinas pueden proporcionar un nivel muy alto de concurrencia con muy poca sobrecarga . Generalmente, en un entorno con hebras tiene como máximo 30-50 subprocesos antes de que se desperdicie la cantidad de gastos indirectos, en realidad la programación de estos subprocesos (por parte del planificador del sistema) reduce significativamente la cantidad de tiempo que los subprocesos realmente hacen un trabajo útil.

Ok, entonces con los hilos puedes tener paralelismo, pero no demasiado paralelismo, ¿no es eso todavía mejor que una co-rutina ejecutándose en un solo hilo? Bueno, no necesariamente Recuerde que una co-rutina todavía puede hacer concurrencia sin sobrecarga del programador, simplemente administra el cambio de contexto en sí mismo.

Por ejemplo, si tiene una rutina que realiza algún trabajo y realiza una operación que sabe bloqueará durante un tiempo (es decir, una solicitud de red), con una co-rutina puede cambiar inmediatamente a otra rutina sin la sobrecarga de incluir el programador del sistema en esta decisión - sí, el programador debe especificar cuándo se pueden cambiar las co-rutinas.

Con muchas rutinas realizando pequeños trabajos y cambiando voluntariamente de una a otra, ha alcanzado un nivel de eficiencia que ningún planificador podría esperar alcanzar. Ahora puede tener miles de coroutines trabajando juntas en lugar de decenas de hilos.

Debido a que sus rutinas ahora cambian entre sí por puntos predeterminados, ahora también puede evitar el bloqueo en estructuras de datos compartidas (porque nunca le diría a su código que cambie a otra corutina en el medio de una sección crítica).

Otro beneficio es el uso de memoria mucho menor. Con el modelo con subprocesos, cada subproceso necesita asignar su propia pila y, por lo tanto, su uso de memoria crece linealmente con la cantidad de subprocesos que tiene. Con las co-rutinas, la cantidad de rutinas que tiene no tiene una relación directa con el uso de su memoria.

Y, por último, las co-rutinas están recibiendo mucha atención porque en algunos lenguajes de programación (como Python) sus hilos no pueden correr en paralelo de todos modos , se ejecutan simultáneamente como corrutinas, pero sin la baja memoria y la sobrecarga de programación libre.


En una palabra: preemption. Las coroneladas actúan como malabaristas que se siguen entregando puntos bien ensayados. Los hilos (hilos verdaderos) se pueden interrumpir en casi cualquier punto y luego se reanudan más tarde. Por supuesto, esto trae consigo todo tipo de problemas de conflicto de recursos, de ahí el famoso GIL - Global Interpreter Lock de Python.

Muchas implementaciones de subprocesos son en realidad más como corrutinas.


Las corutinas son una forma de procesamiento secuencial: solo una se está ejecutando en un momento dado (al igual que las subrutinas, procedimientos AKA, funciones AKA, simplemente pasan el testigo entre sí de forma más fluida).

Los subprocesos son (al menos conceptualmente) una forma de procesamiento concurrente: múltiples subprocesos pueden estar ejecutándose en cualquier momento dado. (Tradicionalmente, en máquinas de un solo núcleo con CPU única, esa simultaneidad se simuló con alguna ayuda del SO; hoy en día, dado que muchas máquinas son multi-CPU y / o multi-core, los hilos se ejecutarán de facto simultáneamente, no solo "conceptualmente").


Primera lectura: Concurrencia vs Paralelismo: ¿Cuál es la diferencia?

La concurrencia es la separación de tareas para proporcionar la ejecución entrelazada. Paralelismo es la ejecución simultánea de múltiples trabajos para aumentar la velocidad. - https://github.com/servo/servo/wiki/Design

Respuesta corta: con los subprocesos, el sistema operativo cambia los subprocesos de manera preventiva según su programador, que es un algoritmo en el kernel del sistema operativo. Con coroutines, el programador y el lenguaje de programación determinan cuándo cambiar las corutinas; en otras palabras, las tareas son multitareas cooperativas al pausar y reanudar funciones en puntos establecidos, normalmente (pero no necesariamente) dentro de un solo hilo.

Respuesta larga: a diferencia de los hilos, que el sistema operativo programa de manera preventiva, los conmutadores de coroutine son cooperativos, lo que significa que el programador (y posiblemente el lenguaje de programación y su tiempo de ejecución) controlan cuándo ocurrirá un cambio.

A diferencia de los hilos, que son preventivos, los interruptores de coroutine son cooperativos (los controles del programador cuando ocurre un cambio). El kernel no está involucrado en los interruptores coroutine. - http://www.boost.org/doc/libs/1_55_0/libs/coroutine/doc/html/coroutine/overview.html

Un lenguaje que admite subprocesos nativos puede ejecutar sus subprocesos (subprocesos de usuario) en los subprocesos del sistema operativo ( subprocesos del kernel ). Cada proceso tiene al menos un hilo de kernel. Los subprocesos de Kernel son como procesos, excepto que comparten espacio de memoria en su proceso de propiedad con todos los demás subprocesos en ese proceso. Un proceso "posee" todos sus recursos asignados, como memoria, identificadores de archivos, sockets, identificadores de dispositivos, etc., y todos estos recursos se comparten entre sus hilos de kernel.

El programador del sistema operativo es parte del kernel que ejecuta cada hilo durante un tiempo determinado (en una sola máquina procesadora). El planificador asigna tiempo (división por tiempos) a cada subproceso, y si el subproceso no termina en ese momento, el planificador lo anticipa (lo interrumpe y cambia a otro subproceso). Se pueden ejecutar varios subprocesos en paralelo en una máquina multiprocesador, ya que cada subproceso se puede (pero no necesariamente) programar en un procesador separado.

En una máquina con un solo procesador, los hilos se dividen en tiempos y se preceden (cambian entre) rápidamente (en Linux, el tiempo predeterminado es de 100ms) lo que los hace concurrentes. Sin embargo, no se pueden ejecutar en paralelo (simultáneamente), ya que un procesador de un solo núcleo solo puede ejecutar una cosa a la vez.

Coroutines y / o generadores se pueden usar para implementar funciones cooperativas. En lugar de ejecutarse en subprocesos del kernel y programados por el sistema operativo, se ejecutan en un único subproceso hasta que ceden o terminan, cediendo a otras funciones según lo determinado por el programador. Los lenguajes con generadores , como Python y ECMAScript 6, se pueden usar para compilar corutinas. Async / await (visto en C #, Python, ECMAscript 7, Rust) es una abstracción construida sobre las funciones del generador que producen futuros / promesas.

En algunos contextos, las corutinas pueden referirse a funciones apiladas mientras que los generadores pueden referirse a funciones apiladas.

Las fibras , los hilos livianos y los hilos verdes son otros nombres para corutinas o cosas parecidas a la corutina. A veces pueden parecer (normalmente a propósito) más parecidos a los hilos del sistema operativo en el lenguaje de programación, pero no se ejecutan en paralelo como hilos reales y funcionan en su lugar como corrutinas. (Puede haber particularidades o diferencias técnicas más específicas entre estos conceptos según el idioma o la implementación).

Por ejemplo, Java tenía " hilos verdes "; estos fueron los hilos que fueron programados por la máquina virtual Java (JVM) en lugar de los hilos del núcleo del sistema operativo subyacente. Estos no se ejecutan en paralelo ni aprovechan múltiples procesadores / núcleos, ¡ya que eso requeriría un hilo nativo! Como no estaban programados por el sistema operativo, eran más como corrutinas que hilos del kernel. Los hilos verdes son los que Java utilizó hasta que se introdujeron los hilos nativos en Java 1.2.

Los hilos consumen recursos. En la JVM, cada subproceso tiene su propia pila, generalmente de 1 MB de tamaño. 64k es la menor cantidad de espacio de pila permitido por subproceso en la JVM. El tamaño de la pila de subprocesos se puede configurar en la línea de comandos para la JVM. A pesar del nombre, los hilos no son libres, debido a sus recursos de uso, como cada hilo que necesita su propia pila, almacenamiento local de subprocesos (si existe) y el costo de la programación de subprocesos / cambio de contexto / invalidación de caché de CPU. Esta es parte de la razón por la cual las coroutinas se han vuelto populares para aplicaciones altamente concurrentes y críticas para el rendimiento.

Mac OS solo permitirá que un proceso asigne aproximadamente 2000 subprocesos, y Linux asignará 8 MB de pila por subproceso y solo admitirá tantos subprocesos que caben en la memoria RAM física.

Por lo tanto, los hilos son los más pesados ​​(en términos de uso de memoria y tiempo de cambio de contexto), luego corutinas, y finalmente los generadores son el peso más ligero.


Depende del idioma que estés usando. Por ejemplo, en Lua son lo mismo (el tipo de variable de una corutina se llama thread ).

Por lo general, las corutinas implementan el rendimiento voluntario donde (usted) el programador decide dónde yield , es decir, le da el control a otra rutina.

En cambio, los hilos se gestionan (detienen e inician) automáticamente por el sistema operativo, e incluso se pueden ejecutar al mismo tiempo en CPU multinúcleo.