Te encontrarás con un par de problemas aquí. El mecanismo de evitación de inanición del planificador verá sus tareas bloqueadas mientras esperan los procesos. Le resultará difícil distinguir entre un hilo estancado y uno simplemente esperando a que se complete un proceso. Como resultado, puede programar nuevas tareas si sus tareas se ejecutan o durante un tiempo prolongado (consulte a continuación). La heurística de ascenso de colina debería tener en cuenta la carga general del sistema, tanto desde su aplicación como desde otras. Simplemente intenta maximizar el trabajo realizado, por lo que agregará más trabajo hasta que el rendimiento general del sistema deje de aumentar y luego retrocederá. No creo esto afectará a su aplicación, pero el problema de evitación de la estabilización probablemente lo hará.
Puede encontrar más detalles en cuanto a cómo funciona todo esto en Parallel Programming with Microsoft®.NET, Colin Campbell, Ralph Johnson, Ade Miller, Stephen Toub (un proyecto anterior es online).
"El grupo de subprocesos .NET gestiona automáticamente el número de trabajadores hilos en la piscina Se añade y elimina las discusiones de acuerdo a una función de heurística El grupo de subprocesos de .NET tiene dos mecanismos principales para inyectar hilos:.. Una mecanismo de inanición evitar que añade trabajador hilos si se ve que no se avanza en elementos en cola y un heurística hillclimbing que trata de maximizar el rendimiento mientras se utiliza como unos hilos como sea posible.
El objetivo de evitar la inanición es prevenir deadlock. Este tipo de interbloqueo puede ocurrir cuando un trabajador th lectura espera un evento de sincronización que solo puede ser satisfecho por un elemento de trabajo que todavía está pendiente en las colas globales o locales del grupo de subprocesos. Si hubiera un número fijo de subprocesos de trabajo, y todos esos subprocesos estuvieran igualmente bloqueados , el sistema no podría realizar ningún progreso adicional. Agregar un nuevo hilo de trabajador resuelve el problema.
Un objetivo de la heurística de ascenso es mejorar la utilización de los núcleos cuando los hilos están bloqueados por E/S u otras condiciones de espera que detienen el procesador. De forma predeterminada, el grupo de subprocesos administrados tiene un subproceso de trabajo por núcleo. Si uno de estos subprocesos de trabajo se convierte en bloqueado, existe la posibilidad de que un núcleo esté subutilizado, dependiendo de en la carga de trabajo general de la computadora.La lógica de inyección de subprocesos no distingue entre un subproceso que está bloqueado y un subproceso que realiza una operación larga y con gran intensidad de procesador. Por lo tanto, siempre que las colas globales o locales del grupo de subprocesos contienen elementos de trabajo pendientes, los elementos de trabajo activos que tardan mucho tiempo en ejecutarse (más de medio segundo) pueden desencadenar la creación de nuevos subprocesos de subprocesador.
El grupo de subprocesos .NET tiene la oportunidad de inyectar subprocesos cada vez que finaliza un elemento de trabajo o en intervalos de 500 milisegundos, lo que sea es más corto. El grupo de subprocesos usa esta oportunidad para intentar agregar subprocesos (o quitárselos), guiado por comentarios de cambios anteriores en el recuento de subprocesos. Si agregar hilos parece ayudar al rendimiento, el grupo de subprocesos agrega más; de lo contrario, reduce el número de subprocesos de trabajo . Esta técnica se llama heurística de alpinismo. Por lo tanto, una razón para mantener cortas las tareas individuales es evitar "detección de inanición", pero otra razón para mantenerlos cortos es dar al conjunto de subprocesos más oportunidades para mejorar el rendimiento por ajustando el recuento de subprocesos. Cuanto menor sea la duración de las tareas individuales , más a menudo el grupo de subprocesos puede medir el rendimiento y ajustar el recuento de hilos en consecuencia.
Para hacer esto concreto, considere un ejemplo extremo. Supongamos que tiene una simulación financiera compleja con 500 operaciones intensivas de procesador , cada una de las cuales toma diez minutos en promedio para completar. Si crea tareas de nivel superior en la cola global para cada de estas operaciones, encontrará que después de unos cinco minutos el grupo de subprocesos crecerá a 500 subprocesos de trabajo. La razón es que el grupo de subprocesos ve todas las tareas como bloqueadas y comienza a agregar nuevos subprocesos a razón de aproximadamente dos subprocesos por segundo.
¿Qué pasa con 500 hilos de trabajo? En principio, nada, si tiene , tiene 500 núcleos para usar y grandes cantidades de memoria del sistema . De hecho, esta es la visión a largo plazo de la computación paralela. Sin embargo, si no tiene tantos núcleos en su computadora, es en una situación donde muchos hilos compiten por segmentos de tiempo. Esta situación se conoce como sobresuscripción de procesador. Permitir que muchos subprocesos intensivos del procesador compitan por el tiempo en un único núcleo agrega carga de conmutación de contexto que puede reducir drásticamente el rendimiento total del sistema . Incluso si no se queda sin memoria, el rendimiento en esta situación puede ser mucho, mucho peor que en el cálculo secuencial. (Cada cambio de contexto toma entre 6,000 y 8,000 ciclos de procesador). El costo del cambio de contexto no es la única fuente de sobrecarga. Un subproceso administrado en .NET consume aproximadamente un megabyte de espacio de pila , independientemente de si se usa ese espacio para las funciones que se están ejecutando actualmente. Se requieren aproximadamente 200,000 ciclos de CPU para crear un nuevo hilo, y aproximadamente 100,000 ciclos para retirar un hilo. Estas son operaciones costosas.
Mientras sus tareas no se toman cada minuto, el algoritmo bajada del grupo de subprocesos con el tiempo se dará cuenta de que tiene demasiados hilos y recortar en su propio acuerdo.Sin embargo, si tiene tareas que ocupan una cadena de trabajo durante muchos segundos, minutos u horas, esa arrojará fuera de la heurística del grupo de subprocesos, y en ese punto usted debería considerar una alternativa.
La primera opción es descomponer la aplicación en tareas más cortas que se completen lo suficientemente rápido para que el grupo de subprocesos consiga controlar la cantidad de subprocesos para un rendimiento óptimo. Una segunda posibilidad es implementar su propio programador de tareas objeto que no realiza la inyección de hilos. Si sus tareas son de larga duración , no necesita un planificador de tareas altamente optimizado porque el costo de la programación será insignificante en comparación con el tiempo de ejecución de la tarea. El programa para desarrolladores de MSDN® tiene un ejemplo de una implementación de programador de tareas simple que limita el grado máximo de concurrencia. Para obtener más información, consulte la sección, "Lectura adicional", al final de este capítulo.
Como último recurso, puede utilizar el método SetMaxThreads a configurar la clase de ThreadPool con un límite superior para el número de subprocesos de trabajo , por lo general igual al número de núcleos (esta es la propiedad Environment.ProcessorCount) . Este límite superior se aplica para todo el proceso, incluyendo todos los dominios de aplicación "
Por supuesto, lo que probablemente debería hacer es cablear un 'TaskCompletionSource' hasta el evento' Process.Exited', y luego tener un método 'TranscodeAsync' que devuelve' Task'. Entonces sería no bloqueante. Entonces puedo tener un control más detallado sobre las tareas sin perder el grano del TPL. –