2009-09-29 19 views
6

Encontré esto en el sitio del Dr. Dobbs hoy en http://www.ddj.com/hpc-high-performance-computing/220300055?pgno=3 Es una buena sugerencia con respecto a la implementación de subprocesos. ¿Cuál es la mejor manera de lograr esto con TThread en Delphi, me pregunto? Gracias BrianCómo programar el número de subprocesos en Delphi

=== del Dr. Dobbs ==============

Hacer multithreading configurable! El número de subprocesos utilizados en un programa siempre debe poder configurarse desde 0 (sin hilos adicionales) hasta un número arbitrario. Esto no solo permite una personalización para un rendimiento óptimo, sino que también demuestra ser una buena herramienta de depuración y, a veces, un salvavidas cuando ocurren condiciones de carrera desconocidas en los sistemas del cliente. Recuerdo más de una situación en la que los clientes pudieron superar errores fatales desconectando el multihilo. Esto, por supuesto, no se aplica solo a la E/S de archivos multiproceso.

Considérese el siguiente pseudocódigo:

int CMyThreadManger::AddThread(CThreadObj theTask) 
{ 
    if(mUsedThreadCount >= gConfiguration.MaxThreadCount()) 
     return theTask.Execute(); // execute task in main thread 
    // add task to thread pool and start the thread 
    ... 
} 

Tal mecanismo no es muy complicado (aunque probablemente será necesario un poco más de trabajo que se muestra aquí), pero a veces es muy eficaz. También se puede usar con librerías de subprocesamiento precompiladas como OpenMP o Intel Threading Building Blocks. Teniendo en cuenta las medidas que se muestran aquí, es una buena idea incluir más de un conteo de hilos configurables (por ejemplo, uno para E/S de archivos y uno para tareas de CPU centrales). El valor predeterminado probablemente sea 0 para archivos de E/S y < número de núcleos encontrados > para tareas de CPU. Pero todos los multihilos deben ser separables. Un enfoque más sofisticado podría incluso incluir algún código para probar el rendimiento de múltiples subprocesos y establecer el número de subprocesos utilizados automáticamente, incluso puede ser individual para las diferentes tareas.

===================

Respuesta

0

por lo general tienen sólo una clase que hereda de TThread, uno que toma '' artículos trabajador de una cola o una pila, y hacer que suspendan cuando no haya más artículos disponibles. El programa principal puede decidir cuántas instancias de este hilo crear y comenzar. (usando este valor de configuración).

Esta cola de elementos de trabajo también debe ser lo suficientemente inteligente como para reanudar hilos suspendidos o crear un nuevo hilo cuando sea necesario (y cuando el límite lo permita), cuando un elemento de trabajo esté en cola o un subproceso haya terminado de procesar un elemento de trabajador .

+5

No es necesario suspender y reanudar los hilos. Deje que cada subproceso espere en un evento o semáforo, o use 'WaitMessage()' con un ciclo de mensajes de subproceso (puede ser necesario para OLE). Hay mucha discusión sobre el tema aquí en SO bajo [delphi], léelo, pero es mejor ignorar los argumentos de las personas que piensan que saben mejor que Embarcadero y los desarrolladores de Microsoft, y tratar de decirte usando 'Suspend()' y 'Resume()' estaría bien. – mghie

5

Crearía una clase abstracta TTask. Esta clase está destinada a ejecutar la tarea. Con el método Ejecutar:

type 

    TTask = abstract class 
    protected 
    procedure DoExecute; virtual; abstract; 
    public 
    procedure Execute; 
    end; 

    TTaskThread = class (TThread) 
    private 
    FTask : TTask; 
    public 
    constructor Create(const ATask: TTask); 
    // Assigns FTask and enables thread, free on terminate. 

    procedure Execute; override; // Calls FTask.Execute. 
end; 

El método Ejecutar comprueba el número de subprocesos. Si no se alcanza el máximo, inicia un hilo usando TTaskThread que llama a DoExecute y como tal ejecuta la tarea en un hilo. Si se alcanza el máximo, se llama directamente a DoExecute.

4

The answer by Gamecat es bueno en cuanto a la clase de tarea abstracta, pero creo que llamar al DoExecute() para una tarea en el hilo de llamada (como también lo hace el artículo) es una mala idea. Siempre ponía en cola las tareas para ser ejecutadas por hilos de fondo, a menos que el enhebrado estuviera deshabilitado por completo, y aquí está el porqué.

considerar el caso siguiente (artificial), donde tiene que ejecutar tres procedimientos independientes vinculados a la CPU:

Procedure1_WhichTakes200ms; 
Procedure2_WhichTakes400ms; 
Procedure3_WhichTakes200ms; 

Para una mejor utilización de su sistema de doble núcleo que quiere ejecutar en dos hilos. Limitaría la cantidad de hilos de fondo a uno, de modo que con el hilo principal tiene tantos hilos como núcleos.

Ahora el primer procedimiento se ejecutará en una cadena de trabajo, y terminará después de 200 milisegundos. El segundo procedimiento comenzará inmediatamente y se ejecutará en el hilo principal, ya que el único hilo de trabajo configurado ya está ocupado, y terminará después de 400 milisegundos. Luego, el último procedimiento se ejecutará en el hilo de trabajo, que ya ha estado durmiendo durante 200 milisegundos ahora, y finalizará después de 200 milisegundos. El tiempo total de ejecución fue de 600 milisegundos, y durante 2/3 de ese tiempo, solo uno de los dos hilos estaba haciendo un trabajo significativo.

Puede reordenar los procedimientos (tareas), pero en la vida real es probablemente imposible saber de antemano cuánto tiempo llevará cada tarea.

Considere ahora la forma común de emplear un grupo de subprocesos. Según la configuración, limitaría el número de subprocesos en el grupo a 2 (número de núcleos), usaría el hilo principal solo para programar los hilos en el grupo y luego esperaría a que se completaran todas las tareas. Con la secuencia anterior de tareas en cola, el hilo 1 tomaría la primera tarea, el segundo hilo tomaría la segunda tarea. Después de 200 milisegundos, la primera tarea se completaría y el primer subproceso de trabajo tomaría la tercera tarea del grupo, que luego estaría vacía. Después de 400 milisegundos, tanto la segunda como la tercera tarea se completarían y el hilo principal sería desbloqueado. Tiempo total de ejecución de 400 milisegundos, con un 100% de carga en ambos núcleos en ese momento.

Al menos para los hilos enlazados a la CPU es de vital importancia tener siempre trabajo en cola para el programador del sistema operativo. Llamar a DoExecute() en el hilo principal interfiere con eso, y no debe hacerse.

Cuestiones relacionadas