¿Diferencia entre una "corutina" y un "hilo"?


Respuestas:


122

Las rutinas 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 se pasan el testigo entre sí de manera más fluida).

Los subprocesos son (al menos conceptualmente) una forma de procesamiento concurrente: se pueden ejecutar múltiples subprocesos en cualquier momento dado. (Tradicionalmente, en máquinas con un solo CPU y un solo núcleo, esa simultaneidad se simulaba con alguna ayuda del sistema operativo, hoy en día, dado que muchas máquinas tienen múltiples CPU y / o múltiples núcleos, los subprocesos se ejecutarán de facto simultáneamente, no solo "conceptualmente").


188

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

La concurrencia es la separación de tareas para proporcionar una ejecución intercalada. El 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 subprocesos, el sistema operativo cambia los subprocesos en ejecución de forma preventiva de acuerdo con su programador, que es un algoritmo en el núcleo del sistema operativo. Con las rutinas, el programador y el lenguaje de programación determinan cuándo cambiar las rutinas; en otras palabras, las tareas son multitarea cooperativas al pausar y reanudar funciones en puntos de ajuste, típicamente (pero no necesariamente) dentro de un solo hilo.

Respuesta larga: en contraste con los subprocesos, que son programados de manera preventiva por el sistema operativo, los interruptores de rutina son cooperativos, lo que significa que el programador (y posiblemente el lenguaje de programación y su tiempo de ejecución) controla cuándo ocurrirá un cambio.

A diferencia de los subprocesos, que son preventivos, los interruptores de rutina son cooperativos (el programador controla cuándo ocurrirá un cambio). El núcleo no está involucrado en los interruptores de rutina. - http://www.boost.org/doc/libs/1_55_0/libs/coroutine/doc/html/coroutine/overview.html

Un lenguaje que admite hilos nativos puede ejecutar sus hilos (hilos de usuario) en los hilos del sistema operativo ( hilos del núcleo ). Cada proceso tiene al menos un hilo del núcleo. Los hilos del kernel son como procesos, excepto que comparten espacio de memoria en su proceso de propiedad con todos los otros hilos 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 del núcleo.

El programador del sistema operativo es parte del núcleo que ejecuta cada subproceso durante un cierto tiempo (en una máquina con un único procesador). El planificador asigna tiempo (división de tiempo) a cada subproceso, y si el subproceso no finaliza dentro de ese tiempo, el planificador lo adelanta (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 programar (pero no necesariamente) en un procesador separado.

En una máquina con un solo procesador, los subprocesos se dividen en tiempo y se reemplazan rápidamente (en Linux, el intervalo de tiempo predeterminado es de 100 ms), 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.

Se pueden utilizar corutinas y / o generadores para implementar funciones cooperativas. En lugar de ejecutarse en subprocesos del núcleo y programados por el sistema operativo, se ejecutan en un solo subproceso hasta que ceden o terminan, cediendo a otras funciones según lo determine el programador. Los lenguajes con generadores , como Python y ECMAScript 6, se pueden usar para construir 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 rutinas 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 las corutinas o cosas similares. A veces pueden parecerse (generalmente a propósito) más como hilos del sistema operativo en el lenguaje de programación, pero no se ejecutan en paralelo como hilos reales y funcionan en cambio como rutinas. (Puede haber particularidades técnicas más específicas o diferencias entre estos conceptos según el lenguaje o la implementación).

Por ejemplo, Java tenía " hilos verdes "; estos eran hilos programados por la máquina virtual Java (JVM) en lugar de ser nativos en los hilos del núcleo del sistema operativo subyacente. Estos no se ejecutaron en paralelo ni aprovecharon múltiples procesadores / núcleos, ¡ya que eso requeriría un hilo nativo! Dado que no fueron programados por el sistema operativo, se parecían más a las rutinas que a los hilos del núcleo. Los hilos verdes son lo que Java usó hasta que los hilos nativos se introdujeron 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 subprocesos no son gratuitos, debido a que utilizan recursos como cada subproceso que necesita su propia pila, almacenamiento local de subprocesos (si corresponde) y el costo de la programación de subprocesos / cambio de contexto / invalidación de caché de la CPU. Esta es parte de la razón por la cual las corutinas se han vuelto populares para aplicaciones críticas de alto rendimiento y altamente concurrentes.

Mac OS solo permitirá que un proceso asigne alrededor de 2000 subprocesos, y Linux asigna 8 MB de pila por subproceso y solo permitirá la cantidad de subprocesos que quepan en la RAM física.

Por lo tanto, los subprocesos son el peso más pesado (en términos de uso de memoria y tiempo de cambio de contexto), luego las rutinas y finalmente los generadores son el peso más liviano.


2
+1, pero esta respuesta podría beneficiarse de algunas referencias.
kojiro

1
Los hilos verdes son algo diferente a las corutinas. ¿No son ellos? Incluso las fibras tienen algunas diferencias. ver programmers.stackexchange.com/questions/254140/…

113

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

En primer lugar, si las corutinas se ejecutan simultáneamente (nunca en paralelo ), ¿por qué alguien las preferiría a los hilos?

La respuesta es que las rutinas pueden proporcionar un nivel muy alto de concurrencia con muy poca sobrecarga . En general, en un entorno de subprocesos, tiene como máximo 30-50 subprocesos antes de que la cantidad de sobrecarga desperdiciada realmente programe estos subprocesos (por el planificador del sistema) reduce significativamente la cantidad de tiempo que los subprocesos realmente realizan un trabajo útil.

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

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

Con muchas rutinas haciendo pequeños trabajos y cambiando voluntariamente entre sí, ha alcanzado un nivel de eficiencia que ningún programador podría alcanzar. Ahora puede tener miles de corutinas trabajando juntas en lugar de decenas de hilos.

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

Otro beneficio es el uso de memoria mucho menor. Con el modelo de subprocesos, cada subproceso debe asignar su propia pila y, por lo tanto, el uso de su memoria aumenta 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 su uso de memoria.

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


2
¿Cómo hacer un cambio a otra tarea en las rutinas cuando nos encontramos con una operación de bloqueo?
Narcisse Doudieu Siewe 01 de

La forma de cambiar a otra tarea es hacer que cualquier operación de bloqueo se realice de forma asincrónica. Esto significa que debe evitar el uso de cualquier operación que realmente bloquee, y solo use operaciones que admitan no bloquear cuando se usa en su sistema de rutina. La única forma de evitar esto es tener corutinas compatibles con el núcleo, como UMS en Windows, por ejemplo, donde salta a su programador cada vez que su "hilo" UMS se bloquea en una llamada al sistema.
retep998

@MartinKonecny ​​¿Los recientes C ++ Threads TS se adhieren al enfoque que mencionó?
Nikos

Entonces, un lenguaje de programación moderno necesitaría tanto Coroutines / Fibers para utilizar eficientemente un solo núcleo de CPU para, por ejemplo, operaciones que no requieren mucha computación como IO y Threads para paralelizar operaciones intensivas de CPU en muchos núcleos para ganar velocidad, ¿verdad?
Mahatma_Fatal_Error

19

En una palabra: preferencia. Las corutinas actúan como malabaristas que se entregan unos a otros puntos bien ensayados. Los subprocesos (subprocesos verdaderos) pueden interrumpirse en casi cualquier punto y luego reanudarse más tarde. Por supuesto, esto trae consigo todo tipo de problemas de conflicto de recursos, de ahí el infame GIL - Global Interpreter Lock de Python.

Muchas implementaciones de hilos son en realidad más como corutinas.


9

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

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

Los subprocesos son administrados automáticamente (detenidos e iniciados) por el sistema operativo, e incluso pueden ejecutarse al mismo tiempo en CPU multinúcleo.


0

12 años tarde a la discusión, pero una corutina tiene la explicación en el nombre. La rutina se puede descomponer en Co y Rutina.

Una rutina en este contexto es solo una secuencia de operaciones / acciones y al ejecutar / procesar una rutina, la secuencia de operaciones se ejecuta una por una en el mismo orden exacto especificado.

Co significa cooperación. Se le pide a una rutina que (o mejor que lo haga) suspenda voluntariamente su ejecución para dar a otras rutinas también la oportunidad de ejecutar. Entonces, una co-rutina se trata de compartir recursos de la CPU (voluntariamente) para que otros puedan usar el mismo recurso que uno está usando.

Un hilo por otro lado no necesita suspender su ejecución. Ser suspendido es completamente transparente para el hilo y el hilo es forzado por el hardware subyacente a suspenderse. También se realiza de una manera que es principalmente transparente para el subproceso, ya que no se le notifica y su estado no se altera, sino que se guarda y luego se restaura cuando se permite que el subproceso continúe.

Una cosa que no es cierta es que las rutinas simultáneas no pueden ejecutarse simultáneamente y las condiciones de carrera no pueden ocurrir. Depende del sistema en el que se ejecutan las co-rutinas y es posible crear imágenes de co-rutinas fácilmente.

No importa cómo se suspendan las co-rutinas. De vuelta en Windows 3.1 int 03 se entrelazó en cualquier programa (o tuvo que colocarse allí) y en C # agregamos rendimiento.

Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.