Los hilos surgen en dos perspectivas: sistemas operativos y lenguajes de programación. En ambos casos, hay alguna variación en los atributos que tiene un hilo.
Una definición mínima de un hilo es que son cosas que suceden en secuencia, una cosa tras otra.
En un modelo de ejecución de máquina típico, cada subproceso tiene su propio conjunto de registros de propósito general y su propio contador de programa. Si la máquina establece un registro específico como un puntero de pila, hay una copia por hilo.
Desde la perspectiva del sistema operativo, lo mínimo que debe hacer un sistema operativo para admitir subprocesos es proporcionar una forma de cambiar entre ellos. Esto puede suceder automáticamente ( multitarea preventiva o solo cuando el hilo realiza una solicitud explícita (multitarea cooperativa; en ese caso, los hilos a veces se denominan fibras ). También hay modelos híbridos con rendimientos preventivos y cooperativos, por ejemplo, preferencia entre hilos de diferentes grupos o tareas pero rendimientos explícitos entre subprocesos del mismo grupo / tarea. Cambiar entre subprocesos implica como mínimo guardar los valores de registro del subproceso anterior y restaurar los valores de registro del nuevo subproceso.
En un sistema operativo multitarea que proporciona aislamiento entre tareas (o procesos , puede tratar estos términos como sinónimos en un contexto de SO), cada tarea tiene sus propios recursos, en particular el espacio de direcciones, pero también archivos abiertos, privilegios, etc. El aislamiento tiene para ser proporcionado por el núcleo del sistema operativo , una entidad que está por encima de los procesos. Cada tarea normalmente tiene al menos un subproceso; una tarea que no ejecuta código no es de mucha utilidad. El sistema operativo puede o no admitir múltiples subprocesos en la misma tarea; por ejemplo, el Unix original no lo hizo. Una tarea aún puede ejecutar varios subprocesos organizando el cambio entre ellos; esto no requiere ningún privilegio especial. Esto se llama " hilos de usuario", Especialmente en un contexto Unix. Hoy en día, la mayoría de los sistemas Unix proporcionan hilos de kernel, en particular porque es la única forma de tener múltiples hilos del mismo proceso ejecutándose en diferentes procesadores.
La mayoría de los recursos del sistema operativo, aparte del tiempo de cálculo, están asociados a tareas, no a subprocesos. Algunos sistemas operativos (por ejemplo, Linux) delimitan explícitamente las pilas, en cuyo caso cada subproceso tiene el suyo; pero hay sistemas operativos en los que el núcleo no sabe nada acerca de las pilas, solo son parte del montón en lo que a él respecta. El núcleo también maneja típicamente un contexto de núcleo para cada hilo, que es una estructura de datos que contiene información sobre lo que el hilo está haciendo actualmente; esto permite que el núcleo maneje múltiples hilos bloqueados en una llamada al sistema al mismo tiempo.
En lo que respecta al sistema operativo, los subprocesos de una tarea ejecutan el mismo código, pero están en diferentes posiciones en ese código (diferentes valores de contador de programa). Puede suceder o no que ciertas partes del código de un programa siempre se ejecuten en subprocesos específicos, pero generalmente hay un código común (por ejemplo, funciones de utilidad) que se puede invocar desde cualquier subproceso. Todos los hilos ven los mismos datos, de lo contrario se considerarían tareas diferentes; Si algunos datos solo pueden ser accedidos por un subproceso en particular, eso generalmente es competencia exclusiva del lenguaje de programación, no del sistema operativo.
En la mayoría de los lenguajes de programación, el almacenamiento se comparte entre subprocesos del mismo programa. Este es un modelo de memoria compartida de programación concurrente; es muy popular, pero también muy propenso a errores, porque el programador debe tener cuidado cuando múltiples hilos pueden acceder a la misma información ya que pueden ocurrir condiciones de carrera . Tenga en cuenta que incluso las variables locales se pueden compartir entre subprocesos: “variable local” (generalmente) significa una variable cuyo nombre solo es válido durante una ejecución de una función, pero otro subproceso puede obtener un puntero a esa variable y acceder a ella.
También hay lenguajes de programación donde cada hilo tiene su propio almacenamiento, y la comunicación entre ellos se realiza mediante el envío de mensajes a través de canales de comunicación. Este es el modelo de transmisión de mensajes de programación concurrente. Erlanges el principal lenguaje de programación que se enfoca en pasar mensajes; su entorno de ejecución tiene un manejo de subprocesos muy liviano, y alienta a los programas escritos con muchos subprocesos de corta duración, en contraste con la mayoría de los otros lenguajes de programación, donde crear un subproceso es una operación relativamente costosa y el entorno de tiempo de ejecución no puede soportar un gran tamaño número de hilos al mismo tiempo. El subconjunto secuencial de Erlang (la parte del lenguaje que ocurre dentro de un hilo, en particular la manipulación de datos) es (principalmente) puramente funcional; por lo tanto, un hilo puede enviar un mensaje a otro hilo que contenga algunos datos y ninguno de ellos debe preocuparse por los datos que el otro hilo modifica mientras lo está utilizando.
Algunos idiomas combinan los dos modelos al ofrecer almacenamiento local de subprocesos, con o sin un sistema de tipos para distinguir la ubicación de almacenamiento local de subprocesos de los globales. El almacenamiento local de subprocesos suele ser una característica conveniente que permite que un nombre variable designe diferentes ubicaciones de almacenamiento en diferentes subprocesos.
Algunos seguimientos (difíciles) que pueden ser de interés para comprender qué son los hilos:
- ¿Cuál es el mínimo que debe hacer un núcleo para admitir múltiples subprocesos?
- En un entorno multiprocesador, ¿qué se necesita para migrar un subproceso de un procesador a otro?
- ¿Qué se necesitaría para implementar múltiples subprocesos cooperativos ( corutinas ) en su lenguaje de programación favorito sin soporte del sistema operativo y sin usar su soporte incorporado, si lo hay? (Tenga en cuenta que la mayoría de los lenguajes de programación carecen de las primitivas necesarias para implementar corutinas dentro de un solo hilo).
- ¿Cómo sería un lenguaje de programación si tuviera concurrencia pero no un concepto (explícito) de hilos? (Ejemplo principal: el cálculo pi ).