threads thread multitarea manejo hilos entre eliminar ejemplos detener con comunicacion como argumentos multithreading hardware cpu-cores

multithreading - thread - multitarea en python



Multithreading: ¿Cuál es el punto de más hilos que núcleos? (17)

Pensé que el objetivo de una computadora multi-core es que podría ejecutar múltiples hilos simultáneamente. En ese caso, si tiene una máquina de cuatro núcleos, ¿de qué sirve tener más de 4 subprocesos en ejecución a la vez? ¿No estarían robando el tiempo el uno al otro?


Aunque ciertamente puede usar hilos para acelerar los cálculos dependiendo de su hardware, uno de sus principales usos es hacer más de una cosa a la vez por razones de facilidad de uso.

Por ejemplo, si tiene que hacer algún procesamiento en segundo plano y también sigue siendo receptivo a la entrada de la interfaz de usuario, puede usar hilos. Sin hilos, la interfaz de usuario se colgaría cada vez que intentara hacer un procesamiento pesado.

También vea esta pregunta relacionada: usos prácticos para hilos


El hecho de que exista un hilo no siempre significa que se está ejecutando activamente. Muchas aplicaciones de subprocesos implican que algunos de los subprocesos se quedan dormidos hasta que es hora de que ellos hagan algo; por ejemplo, la entrada del usuario activa los subprocesos para que se activen, realicen algún procesamiento y vuelvan a dormir.

Básicamente, los hilos son tareas individuales que pueden funcionar independientemente una de la otra, sin necesidad de estar al tanto del progreso de otra tarea. Es muy posible tener más de estos de los que tienes la posibilidad de ejecutar simultáneamente; todavía son útiles por conveniencia, incluso si a veces tienen que esperar en fila uno detrás del otro.


El punto es que la gran mayoría de los programadores no entienden cómo diseñar una máquina de estado. Ser capaz de poner todo en su propio hilo libera al programador de tener que pensar cómo representar eficientemente el estado de diferentes cálculos en curso para que puedan ser interrumpidos y luego reanudados.

Como ejemplo, considere la compresión de video, una tarea que requiere mucha CPU. Si está utilizando una herramienta de interfaz gráfica de usuario, es probable que desee que la interfaz siga siendo receptiva (muestre el progreso, responda a solicitudes de cancelación, cambio de tamaño de la ventana, etc.). Así que diseña el software del codificador para procesar una unidad grande (uno o más marcos) a la vez y ejecutarlo en su propio hilo, separado de la IU.

Por supuesto, una vez que te das cuenta de que hubiera sido bueno poder guardar el estado de codificación en progreso para que puedas cerrar el programa para reiniciar o jugar un juego que consuma muchos recursos, te das cuenta de que deberías haber aprendido a diseñar máquinas de estado desde el comenzando. O eso, o si decide diseñar un nuevo problema de hibernación de procesos en su sistema operativo para que pueda suspender y reanudar aplicaciones individuales en el disco ...


El punto es que, a pesar de no obtener una aceleración real cuando la cuenta de hilos excede el recuento de núcleos, puede usar hilos para desentrañar elementos de lógica que no deberían ser interdependientes.

Incluso en una aplicación moderadamente compleja, el uso de un solo hilo de intentar hacer todo rápidamente hace hash del ''flujo'' de su código. El único hilo pasa la mayor parte del tiempo sondeando esto, verificando eso, llamando rutinas condicionalmente según sea necesario, y se vuelve difícil ver algo más que un aluvión de minucias.

Contraste esto con el caso donde puede dedicar hilos a las tareas para que, al mirar cualquier hilo individual, pueda ver lo que está haciendo ese hilo. Por ejemplo, un hilo puede bloquear la espera en la entrada de un socket, analizar la secuencia en mensajes, filtrar mensajes y, cuando aparece un mensaje válido, pasarlo a algún otro hilo de trabajo. El subproceso de trabajo puede trabajar en entradas de varias otras fuentes. El código para cada uno de estos mostrará un flujo limpio y determinado, sin tener que hacer comprobaciones explícitas de que no hay nada más que hacer.

Particionar el trabajo de esta manera permite que su aplicación dependa del sistema operativo para programar qué hacer luego con la CPU, por lo que no tiene que hacer comprobaciones condicionales explícitas en todas partes de su aplicación sobre qué podría bloquear y qué está listo para procesar.


El uso ideal de los hilos es, de hecho, uno por núcleo.

Sin embargo, a menos que use exclusivamente IO asíncrono / no bloqueante, hay muchas posibilidades de que tenga hilos bloqueados en IO en algún momento, lo que no utilizará su CPU.

Además, los lenguajes de programación típicos dificultan el uso de 1 hilo por CPU. Los idiomas diseñados en torno a la concurrencia (como Erlang) pueden facilitar el uso de subprocesos adicionales.


En respuesta a su primera conjetura: las máquinas multi-core pueden ejecutar simultáneamente múltiples procesos, no solo los múltiples hilos de un único proceso.

En respuesta a su primera pregunta: el punto de múltiples hilos suele ser realizar simultáneamente múltiples tareas dentro de una aplicación. Los ejemplos clásicos en la red son un programa de correo electrónico que envía y recibe correo, y un servidor web que recibe y envía solicitudes de página. (Tenga en cuenta que es esencialmente imposible reducir un sistema como Windows para ejecutar solo un hilo o incluso un solo proceso. Ejecute el Administrador de tareas de Windows y verá una larga lista de procesos activos, muchos de los cuales ejecutarán varios subprocesos. )

En respuesta a su segunda pregunta: la mayoría de los procesos / subprocesos no están vinculados a la CPU (es decir, no se ejecutan continuamente ni ininterrumpidamente), sino que se detienen y esperan con frecuencia para que la E / S finalice. Durante esa espera, otros procesos / subprocesos pueden ejecutarse sin "robar" del código de espera (incluso en una única máquina central).


Estoy totalmente en desacuerdo con la afirmación de @koryory de que el número ideal es un hilo por CPU.

Piénselo de esta manera: ¿por qué tenemos sistemas operativos multiprocesador? Durante la mayor parte de la historia de la computadora, casi todas las computadoras tenían una CPU. Sin embargo, a partir de la década de 1960, todas las computadoras "reales" tenían sistemas operativos de multiprocesamiento (también llamados multitareas).

Ejecutas múltiples programas para que uno pueda ejecutarse mientras que otros están bloqueados para cosas como IO.

dejemos de lado argumentos sobre si las versiones de Windows antes de NT eran multitarea. Desde entonces, cada SO real tenía multitareas. Algunos no lo exponen a los usuarios, pero está ahí de todos modos, haciendo cosas como escuchar la radio del teléfono celular, hablar con el chip GPS, aceptar la entrada del mouse, etc.

Los hilos son solo tareas que son un poco más eficientes. No hay una diferencia fundamental entre una tarea, proceso e hilo.

Una CPU es algo terrible que perder, así que tenga muchas cosas listas para usar cuando pueda.

Estoy de acuerdo en que con la mayoría de los lenguajes de procedimiento, C, C ++, Java, etc., escribir un código de seguridad adecuado es mucho trabajo. Con 6 CPU centrales en el mercado hoy en día, y 16 CPU centrales no muy lejos, espero que la gente se aleje de estos lenguajes antiguos, ya que el multi-threading es un requisito cada vez más crítico.

El desacuerdo con @kyoryu es solo en mi humilde opinión, el resto es un hecho.


Imagine un servidor web que tiene que atender un número arbitrario de solicitudes. Debe atender las solicitudes en paralelo porque, de lo contrario, cada nueva solicitud debe esperar hasta que se completen todas las demás solicitudes (incluido el envío de la respuesta por Internet). En este caso, la mayoría de los servidores web tienen menos núcleos que la cantidad de solicitudes que suelen atender.

También lo hace más fácil para el desarrollador del servidor: solo tiene que escribir un programa de subprocesos que atienda una solicitud, no tiene que pensar en almacenar varias solicitudes, el orden en que las atiende, etc.


La forma en que se diseñan algunas API, no tiene más remedio que ejecutarlas en un hilo separado (cualquier cosa con operaciones de bloqueo). Un ejemplo serían las bibliotecas HTTP de Python (AFAIK).

Por lo general, esto no es un gran problema (si es un problema, el sistema operativo o la API deben enviarse con un modo de funcionamiento asincrónico alternativo, es decir: select(2) ), porque probablemente significa que el subproceso va a estar durmiendo durante esperando la finalización de E / S. Por otro lado, si algo está haciendo una gran computación, tienes que ponerlo en un hilo por separado, por ejemplo, el hilo de la GUI (a menos que disfrutes de la multiplexación manual).


La mayoría de las respuestas anteriores hablan sobre el rendimiento y la operación simultánea. Voy a abordar esto desde un ángulo diferente.

Tomemos el caso de, digamos, un programa de emulación de terminal simplista. Tienes que hacer las siguientes cosas:

  • esté atento a los caracteres entrantes del sistema remoto y muéstrelos
  • Esté atento a las cosas que provienen del teclado y envíelos al sistema remoto

(Los emuladores de terminal real hacen más cosas, incluso hacer eco de las cosas que tipeas en la pantalla también, pero por ahora las pasaremos por alto).

Ahora, el ciclo para leer desde el control remoto es simple, según el siguiente pseudocódigo:

while get-character-from-remote: print-to-screen character

El ciclo para monitorear el teclado y enviarlo también es simple:

while get-character-from-keyboard: send-to-remote character

El problema, sin embargo, es que tienes que hacer esto simultáneamente. El código ahora tiene que verse más como esto si no tiene subprocesos:

loop: check-for-remote-character if remote-character-is-ready: print-to-screen character check-for-keyboard-entry if keyboard-is-ready: send-to-remote character

La lógica, incluso en este ejemplo deliberadamente simplificado que no toma en cuenta la complejidad de las comunicaciones en el mundo real, está bastante ofuscada. Con el enhebrado, sin embargo, incluso en un solo núcleo, los dos bucles de pseudocódigo pueden existir independientemente sin entrelazar su lógica. Debido a que ambos subprocesos serán en su mayoría vinculados a E / S, no ejercen una gran carga en la CPU, a pesar de que, estrictamente hablando, consumen más recursos de la CPU que el bucle integrado.

Ahora, por supuesto, el uso en el mundo real es más complicado que el anterior. Pero la complejidad del ciclo integrado aumenta exponencialmente a medida que agrega más preocupaciones a la aplicación. La lógica se vuelve cada vez más fragmentada y debes comenzar a utilizar técnicas como máquinas de estados, corotines, etc. para que las cosas sean más manejables. Manejable, pero no legible. Threading mantiene el código más legible.

Entonces, ¿por qué no usarías el enhebrado?

Bueno, si sus tareas están unidas a la CPU en lugar de a la de E / S, el enhebrado de hecho ralentiza su sistema. El rendimiento sufrirá. Mucho, en muchos casos. ("Thrashing" es un problema común si suelta demasiados subprocesos enlazados a la CPU. Terminará gastando más tiempo cambiando los hilos activos que usted ejecutando los contenidos de los subprocesos). Además, una de las razones por las que la lógica anterior es tan simple es que he elegido deliberadamente un ejemplo simplista (y poco realista). Si desea hacer eco de lo que se tipeó en la pantalla, entonces tendrá un nuevo mundo de dolor al introducir el bloqueo de los recursos compartidos. Con solo un recurso compartido, esto no es tanto un problema, sino que se convierte en un problema cada vez más grande, ya que tiene más recursos para compartir.

Entonces, al final, enhebrar se trata de muchas cosas. Por ejemplo, se trata de hacer que los procesos vinculados a E / S sean más receptivos (incluso si son menos eficientes en general) como algunos ya han dicho. También se trata de hacer que la lógica sea más fácil de seguir (pero solo si minimizas el estado compartido). Se trata de muchas cosas, y usted tiene que decidir si sus ventajas superan sus desventajas caso por caso.


La respuesta gira en torno al propósito de los hilos, que es el paralelismo: ejecutar varias líneas de ejecución separadas a la vez. En un sistema "ideal", tendría un hilo ejecutándose por núcleo: sin interrupción. En realidad este no es el caso. Incluso si tiene cuatro núcleos y cuatro hilos de trabajo, su proceso y sus hilos se cambiarán constantemente para otros procesos y subprocesos. Si está ejecutando cualquier SO moderno, cada proceso tiene al menos un hilo, y muchos tienen más. Todos estos procesos se ejecutan a la vez. Es probable que tenga varios cientos de hilos ejecutándose en su máquina ahora mismo. Nunca se producirá una situación en la que se ejecute un hilo sin que se le "robe" el tiempo. (Bueno, podría hacerlo si se ejecuta en tiempo real , si usa un sistema operativo en tiempo real o, incluso en Windows, usa una prioridad de subprocesos en tiempo real. Pero es raro).

Con eso como fondo, la respuesta: Sí, más de cuatro subprocesos en una verdadera máquina de cuatro núcleos pueden darle una situación en la que ''roban tiempo el uno al otro'', pero solo si cada subproceso individual necesita 100% de CPU . Si un hilo no funciona al 100% (como un hilo de UI podría no serlo, o un hilo haciendo una pequeña cantidad de trabajo o esperando algo más) entonces otro hilo que se está programando es realmente una buena situación.

En realidad es más complicado que eso:

  • ¿Qué pasa si tiene cinco bits de trabajo que todos deben hacerse a la vez? Tiene más sentido ejecutarlos todos a la vez, que ejecutar cuatro de ellos y luego ejecutar el quinto más tarde.

  • Es raro que un hilo realmente necesite 100% de CPU. En el momento en que utiliza la E / S de disco o red, por ejemplo, es posible que tarde tiempo en esperar sin hacer nada útil. Esta es una situación muy común.

  • Si tiene trabajo que necesita ejecutarse, un mecanismo común es usar un threadpool. Puede parecer lógico tener el mismo número de subprocesos que núcleos, pero el grupo de subprocesos .Net tiene hasta 250 subprocesos disponibles por procesador . No estoy seguro de por qué lo hacen, pero creo que tiene que ver con el tamaño de las tareas que se dan para ejecutar en los hilos.

Así que: robar tiempo no es algo malo (y tampoco es realmente un robo: es cómo se supone que funciona el sistema). Escribe tus programas multiproceso según el tipo de trabajo que harán los hilos, que pueden no ser CPU -ligado. Calcule la cantidad de hilos que necesita según el perfil y la medida. Puede que le resulte más útil pensar en términos de tareas o trabajos, en lugar de hilos: escriba objetos de trabajo y entréguelos a un grupo para ejecutar. Finalmente, a menos que su programa sea realmente crítico para el rendimiento, no se preocupe demasiado :)


Los subprocesos pueden ayudar con la capacidad de respuesta en aplicaciones de interfaz de usuario. Además, puede usar hilos para obtener más trabajo de sus núcleos. Por ejemplo, en un solo núcleo, puede tener un hilo haciendo IO y otro haciendo algo de cálculo. Si tuviera un único hilo, el núcleo podría estar esencialmente inactivo esperando a que se complete el IO. Ese es un ejemplo de alto nivel, pero los hilos definitivamente se pueden usar para golpear su CPU un poco más.


Muchos hilos estarán dormidos, esperando la entrada del usuario, E / S y otros eventos.


Sé que esta es una vieja pregunta con muchas buenas respuestas, pero estoy aquí para señalar algo que es importante en el entorno actual:

Si desea diseñar una aplicación para multi-threading, no debe diseñar para una configuración de hardware específica. La tecnología de la CPU ha avanzado con bastante rapidez durante años, y los recuentos de núcleos aumentan constantemente. Si deliberadamente diseñas tu aplicación de modo que solo use 4 hilos, entonces te estás restringiendo potencialmente en un sistema octa-core (por ejemplo). Ahora, incluso los sistemas de 20 núcleos están disponibles comercialmente, por lo que un diseño definitivamente está haciendo más daño que bien.


Si un hilo está esperando un recurso (como cargar un valor de RAM en un registro, E / S de disco, acceso a la red, iniciar un nuevo proceso, consultar una base de datos o esperar la entrada del usuario), el procesador puede trabajar en un hilo diferente, y regresar al primer hilo una vez que el recurso esté disponible. Esto reduce el tiempo que la CPU permanece inactiva, ya que la CPU puede realizar millones de operaciones en lugar de estar inactiva.

Considere un hilo que necesita leer datos de un disco duro. En 2014, un núcleo de procesador típico opera a 2.5 GHz y puede ejecutar 4 instrucciones por ciclo. Con un tiempo de ciclo de 0.4 ns, el procesador puede ejecutar 10 instrucciones por nanosegundo. Con los tiempos típicos de búsqueda del disco duro mecánico son de alrededor de 10 milisegundos, el procesador es capaz de ejecutar 100 millones de instrucciones en el tiempo que lleva leer el valor del disco duro. Puede haber mejoras de rendimiento significativas con discos duros con un pequeño caché (4 MB de búfer) y unidades híbridas con unos pocos GB de almacenamiento, ya que la latencia de datos para lecturas secuenciales o lecturas de la sección híbrida puede ser varios órdenes de magnitud más rápida.

Un núcleo de procesador puede alternar entre subprocesos (el costo de pausa y reanudación de un subproceso es de alrededor de 100 ciclos de reloj) mientras que el primer subproceso espera una entrada de alta latencia (cualquier cosa más costosa que los registros (1 reloj) y RAM (5 nanosegundos)). E / S de disco, acceso a la red (latencia de 250 ms), lectura de datos de un CD o un bus lento, o una llamada a la base de datos. Tener más hilos que núcleos significa que se puede hacer un trabajo útil mientras se resuelven las tareas de alta latencia.

La CPU tiene un programador de subprocesos que asigna prioridad a cada subproceso y permite que un subproceso duerma y luego reanude después de un tiempo predeterminado. El trabajo del programador de hilos es reducir el golpeteo, lo que ocurriría si cada hilo ejecutara solo 100 instrucciones antes de volver a dormir. La sobrecarga de los hilos de conmutación reduciría el rendimiento total útil del núcleo del procesador.

Por esta razón, es posible que desee dividir su problema en una cantidad razonable de hilos. Si estaba escribiendo código para realizar la multiplicación de matriz, la creación de un subproceso por celda en la matriz de salida puede ser excesiva, mientras que un subproceso por fila o por n filas en la matriz de salida puede reducir el costo general de creación, pausa y reanudación de subprocesos.

Esta es también la razón por la cual la predicción de ramas es importante. Si tiene una instrucción if que requiere cargar un valor desde la RAM, pero el cuerpo de las instrucciones if y else usa valores ya cargados en los registros, el procesador puede ejecutar una o ambas ramas antes de que la condición haya sido evaluada. Una vez que la condición retorna, el procesador aplicará el resultado de la rama correspondiente y descartará la otra. Realizar aquí un trabajo potencialmente inútil es probablemente mejor que cambiar a un hilo diferente, lo que podría llevar a una paliza.

A medida que nos alejamos de los procesadores de un solo núcleo de alta velocidad de reloj para procesadores multinúcleo, el diseño de chips se enfocó en incluir más núcleos por dado, mejorar el intercambio de recursos en el chip entre núcleos, mejores algoritmos de predicción de bifurcaciones, mejor sobrecarga de conmutación de hilos y una mejor programación de hilos.


Un hilo es una abstracción que le permite escribir código tan simple como una secuencia de operación, ignorando que el código se ejecuta entrelazado con otro código.


Un procesador, o CPU, es el chip físico que está conectado al sistema. Un procesador puede tener múltiples núcleos (un núcleo es la parte del chip que es capaz de ejecutar instrucciones). Un núcleo puede aparecer en el sistema operativo como múltiples procesadores virtuales si es capaz de ejecutar simultáneamente múltiples hilos (un hilo es una secuencia única de instrucciones).

Un proceso es otro nombre para una aplicación. En general, los procesos son independientes entre sí. Si un proceso muere, no causa que otro proceso también muera. Es posible que los procesos se comuniquen o compartan recursos como memoria o E / S.

Cada proceso tiene un espacio de direcciones y una pila separados. Un proceso puede contener múltiples hilos, cada uno capaz de ejecutar instrucciones simultáneamente. Todos los hilos en un proceso comparten el mismo espacio de direcciones, pero cada hilo tendrá su propia pila.

Esperemos que con estas definiciones y una mayor investigación utilizando estos fundamentos ayudará a su comprensión.