.net multithreading design architecture threadpool

.net - Thread vs ThreadPool



multithreading design (10)

El almacenamiento local de subprocesos no es una buena idea con grupos de subprocesos. Le da a los hilos una "identidad"; no todos los hilos son iguales nunca más Ahora los grupos de subprocesos son especialmente útiles si solo necesitas un montón de subprocesos idénticos, listos para hacer tu trabajo sin gastos generales de creación.

¿Cuál es la diferencia entre usar un nuevo hilo y usar un hilo del grupo de hilos? ¿Qué beneficios de rendimiento existen y por qué debería considerar usar un hilo del grupo en lugar de uno creado explícitamente? Estoy pensando específicamente en .NET aquí, pero los ejemplos generales están bien.


El grupo de subprocesos proporcionará beneficios para las operaciones frecuentes y relativamente cortas de

  • Reutilizar hilos que ya se han creado en lugar de crear nuevos (un proceso costoso)
  • Limitar la tasa de creación de subprocesos cuando hay una ráfaga de solicitudes de nuevos elementos de trabajo (creo que esto solo está en .NET 3.5)

    • Si pone en cola 100 tareas de grupo de subprocesos, solo usará tantos subprocesos como ya se hayan creado para atender estas solicitudes (digamos 10 por ejemplo). El grupo de subprocesos realizará comprobaciones frecuentes (creo que cada 500 ms en 3.5 SP1) y si hay tareas en cola, creará un nuevo subproceso. Si sus tareas son rápidas, entonces la cantidad de hilos nuevos será pequeña y reutilizar los 10 o más hilos para las tareas cortas será más rápido que crear 100 hilos por adelantado.

    • Si su carga de trabajo consistentemente tiene un gran número de solicitudes de grupo de subprocesos, el grupo de subprocesos se sintonizará a su carga de trabajo creando más subprocesos en el grupo mediante el proceso anterior para que haya un mayor número de subprocesos disponibles para procesar solicitudes

    • marque Here para obtener más información detallada sobre cómo funciona el grupo de subprocesos bajo el capó

Crear un nuevo hilo usted mismo sería más apropiado si el trabajo fuera relativamente largo (probablemente alrededor de uno o dos segundos, pero depende de la situación específica)

@Krzysztof - Los subprocesos del grupo de subprocesos son subprocesos de fondo que se detendrán cuando finalice el subproceso principal. Los subprocesos creados manualmente están en primer plano de forma predeterminada (seguirán ejecutándose después de que el subproceso principal haya finalizado), pero se pueden establecer en segundo plano antes de llamar a Iniciar en ellos.


En general (nunca he usado .NET), se usaría un grupo de subprocesos para la administración de recursos. Permite que las restricciones sean configuradas en su software. También se puede hacer por motivos de rendimiento, ya que la creación de nuevos hilos puede ser costosa.

También puede haber razones específicas del sistema. En Java (nuevamente no sé si esto se aplica a .NET), el administrador de los hilos puede aplicar variables específicas de subprocesos a medida que se extrae cada subproceso del grupo, y deshacerlos cuando se devuelven (forma común de pasar algo así como una identidad).

Restricción de ejemplo: solo tengo 10 conexiones de db, por lo que solo permitiría que 10 subprocesos de trabajo accedan a la base de datos.

Esto no significa que no deba crear sus propios hilos, pero existen condiciones bajo las cuales tiene sentido usar un conjunto.


Era curioso sobre el uso relativo de recursos para estos y obtuve un punto de referencia en mi computadora portátil Intel i5 de doble núcleo 2012 usando la versión de lanzamiento de .NET 4.0 en Windows 8. Thread Pools tomó un promedio de 0.035ms para comenzar donde Threads tomó un promedio de 5.06 Sra. En otras palabras, el hilo en el grupo comenzó aproximadamente 300 veces más rápido para un gran número de hilos de corta duración. Al menos en los hilos del rango probado (100-2000), el tiempo total por hilo parecía bastante constante.

Este es el código que fue referenciado:

for (int i = 0; i < ThreadCount; i++) { Task.Run(() => { }); } for (int i = 0; i < ThreadCount; i++) { var t = new Thread(() => { }); t.Start(); }


La principal necesidad de los subprocesos de itpool es manejar pequeñas tareas breves que se espera que se completen casi al instante. Los manejadores de interrupciones de hardware a menudo se ejecutan en un contexto de apilamiento que no sería adecuado para código no kernel, pero un controlador de interrupción de hardware puede descubrir que se debe ejecutar una devolución de llamada de finalización de E / S en modo usuario tan pronto como sea posible. Crear un nuevo hilo con el fin de ejecutar tal cosa sería una sobrecarga masiva. Tener algunos subprocesos creados previamente que pueden enviarse para ejecutar devoluciones de llamadas de finalización de E / S u otras cosas similares es mucho más eficiente.

Un aspecto clave de tales subprocesos es que si los métodos de finalización de E / S siempre se completan de manera prácticamente instantánea y nunca se bloquean, y el número de tales subprocesos que se están ejecutando actualmente es al menos igual al número de procesadores, la única forma en que cualquier otro subproceso podría ejecutarse antes de que uno de los métodos antes mencionados termine si uno de los otros métodos bloquea o su tiempo de ejecución excede un segmento de tiempo de subprocesamiento normal; ninguno de estos debería suceder muy a menudo si el grupo de subprocesos se usa como se pretendía.

Si no se puede esperar que un método salga dentro de los 100 ms más o menos de cuando comienza la ejecución, el método debe ejecutarse por algún otro medio que no sea el grupo de subprocesos principal. Si se tienen que realizar muchas tareas que consumen mucha CPU pero no se bloquean, puede ser útil enviarlas utilizando un conjunto de subprocesos de aplicaciones (uno por núcleo de CPU) que es independiente del grupo de subprocesos "principal", ya que más hilos que núcleos serán contraproducentes cuando se ejecutan tareas intensivas de CPU sin bloqueo. Sin embargo, si un método tardará un segundo o más en ejecutarse y pasará la mayor parte del tiempo bloqueado, es probable que el método se ejecute en un subproceso dedicado y, con seguridad, no se ejecute en un subproceso de subproceso principal. Si una operación de larga duración necesita ser activada por algo como una devolución de llamada de E / S, uno debe iniciar un hilo para la operación de larga ejecución antes de la devolución de llamada y esperar en un monitor que la devolución de llamada emita, o bien hacer que la devolución de llamada ejecute un nuevo hilo para realizar la operación mientras sale la devolución de llamada, devolviendo efectivamente su propio hilo al grupo de hilos.


Mira aquí un hilo anterior:

¿Cuándo no debería usar ThreadPool en .Net?

El resumen es que Threadpool es bueno si necesita generar muchos hilos de corta duración, mientras que el uso de Threads le da un poco más de control.


Si necesita muchos hilos, probablemente quiera usar un ThreadPool. Reutilizan subprocesos que le ahorran la sobrecarga de la creación de subprocesos.

Si solo necesitas un hilo para hacer algo, el hilo probablemente sea el más fácil.


The .NET managed threadpool: -

  • Tamaños en sí basados ​​en la carga de trabajo actual y el hardware disponible
  • Contiene subprocesos de trabajo y subprocesos de puerto de finalización (que se usan específicamente para dar servicio a IO)
  • Está optimizado para un gran número de operaciones relativamente efímeras

Existen otras implementaciones de grupo de subprocesos que podrían ser más apropiadas para las operaciones de larga ejecución.

Específicamente, use un grupo de subprocesos para evitar que su aplicación cree demasiados subprocesos. La característica más importante de un threadpool es la cola de trabajos. Es decir, una vez que su máquina está lo suficientemente ocupada, el grupo de subprocesos pondrá en cola las solicitudes en lugar de engendrar inmediatamente más subprocesos.

Por lo tanto, si crea una cantidad pequeña y limitada de hilos, créelos usted mismo. Si no puede determinar por adelantado cuántos subprocesos se pueden crear (p. Ej., Se crean en respuesta a IO entrantes) y su trabajo será de corta duración, utilice el grupo de subprocesos. Si no sabe cuántos, pero su trabajo será de larga duración, no hay nada en la plataforma que lo ayude, pero es posible que pueda encontrar implementaciones de subprocesos alternativos que quepan.


Usar una agrupación es una buena idea, si no sabe o no puede controlar cuántos subprocesos se crearán.

Solo tiene un problema con un formulario que utiliza el hilo para actualizar algún campo de una base de datos en un evento de cambio de posición de un control de lista (evite Freez). Mi usuario tardó 5 minutos en tener un error de la base de datos (demasiadas conexiones con Access) porque estaba cambiando la posición de la lista demasiado rápido ...

Sé que hay otra forma de resolver el problema de base (incluido el no uso de acceso), pero la puesta en común es un buen comienzo.


además

new Thread().Start()

genera hilo de primer plano que no morirá si cierra su programa. Los hilos de ThreadPool son hilos de fondo que mueren cuando cierras la aplicación.