2010-06-27 12 views
6

Estoy creando una aplicación de consola que necesita ejecutar varios subprocesos para realizar una tarea. Mi problema es que los hilos se están ejecutando uno tras otro (thread1 start -> work -> end y ONLY then start thread2) en lugar de ejecutar todo al mismo tiempo. Además, no quiero que más de 10 subprocesos funcionen al mismo tiempo (problemas de rendimiento). Bellow es un código de ejemplo de aplicación de consola y del módulo de datos utilizado. mi aplicación está trabajando de la misma manera. He utilizado un módulo de datos porque después de que los hilos terminen, debo completar una base de datos con esas informaciones. También hay comentarios en el código para explicar cuál es el motivo para hacer algo.¿Por qué los hilos se ejecutan en serie en esta aplicación de consola?

aplicación de código consola:

program Project2; 

{$APPTYPE CONSOLE} 

uses 
    SysUtils, 
    Unit1 in 'Unit1.pas' {DataModule1: TDataModule}; 

var dm:TDataModule1; 
begin 
    dm:=TDataModule1.Create(nil); 
    try 
    dm.execute; 
    finally 
    FreeAndNil(dm); 
    end; 
end. 

y módulo de datos de código

unit Unit1; 

interface 

uses 
    SysUtils, Classes, SyncObjs, Windows, Forms; 

var FCritical: TRTLCriticalSection;//accessing the global variables 

type 
    TTestThread = class(TThread) 
    protected 
    procedure Execute;override; 
    end; 
    TDataModule1 = class(TDataModule) 
    procedure DataModuleCreate(Sender: TObject); 
    procedure DataModuleDestroy(Sender: TObject); 
    private 
    { Déclarations privées } 
    public 

    procedure execute; 
    procedure CreateThread(); 
    procedure Onterminatethrd(Sender: TObject); 
    end; 

var 
    DataModule1  : TDataModule1; 
    FthreadCount  : Integer; //know how many threads are running 


implementation 

{$R *.dfm} 

{ TTestThread } 

procedure TTestThread.Execute; 
var 
    f     : TextFile; 
    i     : integer; 
begin 
    EnterCriticalSection(fcritical); 
    AssignFile(f, 'd:\a' + inttostr(FthreadCount) + '.txt'); 
    LeaveCriticalSection(fcritical); 
    Rewrite(f); 
    try 
    i := 0; 
    while i <= 1000000 do // do some work... 
     Inc(i); 
    Writeln(f, 'done'); 
    finally 
    CloseFile(f); 
    end; 
end; 

{ TDataModule1 } 

procedure TDataModule1.CreateThread; 
var 
    aThrd    : TTestThread; 
begin 
    aThrd := TTestThread.Create(True); 
    aThrd.FreeOnTerminate := True; 
    EnterCriticalSection(fcritical); 
    Inc(FthreadCount); 
    LeaveCriticalSection(fcritical); 
    aThrd.OnTerminate:=Onterminatethrd; 
    try 
    aThrd.Resume; 
    except 
    FreeAndNil(aThrd); 
    end; 
end; 

procedure TDataModule1.Onterminatethrd(Sender: TObject); 
begin 
    EnterCriticalSection(fcritical); 
    Dec(FthreadCount); 
    LeaveCriticalSection(fcritical); 
end; 

procedure TDataModule1.DataModuleCreate(Sender: TObject); 
begin 
    InitializeCriticalSection(fcritical); 
end; 

procedure TDataModule1.DataModuleDestroy(Sender: TObject); 
begin 
    DeleteCriticalSection(fcritical); 
end; 

procedure TDataModule1.execute; 
var 
    i     : integer; 
begin 
    i := 0; 
    while i < 1000 do 
    begin 
    while (FthreadCount = 10) do 
     Application.ProcessMessages;//wait for an thread to finish. max threads at a //time =10 

    CreateThread; 

    EnterCriticalSection(fcritical); 
    Inc(i); 
    LeaveCriticalSection(fcritical); 

    while FthreadCount > 0 do //wait for all threads to finish in order to close the //main thread 
    begin 
     Application.ProcessMessages; 
     CheckSynchronize; 
    end; 
    end; 
end; 

end. 

así, como he dicho el problema es que mis hilos se ejecutan uno tras otro, en lugar de trabajar todos en la Mismo tiempo. También he visto que a veces solo funcionaba el primer subproceso, después de eso todo el resto solo creaba y terminaba. en mi aplicación, todo el código está protegido por try-excepts, pero no se generan errores.

¿Puede alguien darme un consejo?

+3

Una buena forma de garantizar que no se ejecuten más de 10 subprocesos al mismo tiempo es utilizar un * semáforo *. Adquirirlo antes de crear un hilo, y hacer que cada hilo lo libere al finalizarlo. Si ya hay 10 subprocesos en ejecución, el undécimo intento de adquirir el semáforo se bloqueará hasta que termine otro subproceso. Entonces no necesita los bucles de espera de espera de ProcessMessages. Esos bucles no hacen más que consumir tiempo de CPU porque tu aplicación nunca * envía * ningún mensaje, por lo que nunca habrá nada que procesar. –

+0

Otro paso que puede tomar es llamar a 'CheckSynchronize' cuando realmente hay algo con lo que sincronizar. Con su configuración de semáforo, llame a MsgWaitForMultipleObjects en el control del semáforo y la variable global 'SyncEvent'. Lea sobre esto en Classes.pas. Si se señala el semáforo, crea un nuevo hilo. Si se señala el evento, llame a CheckSynchronize. –

+0

Rob, muchas gracias por la información. He leído acerca de los semáforos, pero no estoy seguro de que entiendo completamente cómo debería implementarlo. por lo que he leído, necesito usar WaitForSingleObject (handle, infinite) para que el hilo termine todo el ciclo, pero ¿cómo puedo saber cuándo iniciar otro hilo? He visto algunos ejemplos, así que creo que debo crear un semáforo con 10 hilos, pero al disminuir la forma en que estoy creando un nuevo hilo y almorzarlo en semáforo? ¿alguien me puede dar un pequeño ejemplo basado en mi ejemplo? gracias de antemano! – RBA

Respuesta

6

Por lo menos se debería poner

while FthreadCount > 0 do //wait for all threads to finish in order to close the //main thread 
begin 
    Application.ProcessMessages; 
    CheckSynchronize; 
end; 

fuera del bucle principal. Este ciclo de espera es lo que está causando el retraso. Para cada entero i del mainloop, espera hasta que FThreadCount caiga a cero.

En una nota al margen: normalmente no es necesario proteger las variables locales con secciones críticas. Aunque tener mensajes de proceso allí puede arruinar las cosas ya que puede causar la reentrada.

-1

Tengo una unidad que hace exactamente lo que necesita. Sólo tienes que descargar desde:

Cromis.Threading

su interior tiene dos clases:

  1. TTaskPool: piscina de Tareas. Una manera fácil de hacer cosas asincrónicas.
  2. TTaskQueue: cola de tareas asincrónicas. Funciona como una cola FIFO estándar.

El TTaskQueue se puede utilizar de forma independiente con hilos sencillos de vanila, por ejemplo. Bloquea dentro de un solo hilo y pone en cola las solicitudes.

Si esto no es suficiente, se puede comprobar el OmniThreadLibrary en:

OmniThreadLibrary

Esta es una poderosa librería de hilos, muy superior a lo que tengo. Pero también es más complicado de usar (pero aún es muy fácil en comparación con el enhebrado clásico).

+0

No creo que quiera que los hilos se ejecuten uno tras otro. Él no está seguro de por qué no están corriendo en paralelo. –

+0

Uh, leí esto muy sloopy. Gracias Bruce – Runner

1

He seguido la sugerencia de Marjan y parece que el siguiente código funciona correctamente. Estoy respondiendo a mi propia pregunta para proporcionar un código de respuesta, que otros pueden analizar y corregir si es necesario.

unit Unit1; 

interface 

uses 
    SysUtils, Classes, SyncObjs, Windows, Forms, Dialogs; 

var FCritical: TRTLCriticalSection; 

type 
    TTestThread = class(TThread) 
    protected 
    procedure Execute;override; 
    end; 
    TDataModule1 = class(TDataModule) 
    procedure DataModuleCreate(Sender: TObject); 
    procedure DataModuleDestroy(Sender: TObject); 
    private 
    { Déclarations privées } 
    public 

    procedure execute; 
    procedure CreateThread(); 
    procedure Onterminatethrd(Sender: TObject); 
    end; 

var 
    DataModule1  : TDataModule1; 
    FthreadCount  : Integer; 


implementation 

{$R *.dfm} 

{ TTestThread } 

procedure TTestThread.Execute; 
var 
    f     : TextFile; 
    i     : integer; 

begin 
AssignFile(f, 'd:\a\a' + inttostr(FthreadCount) + '.txt'); 
if fileexists('d:\a\a' + inttostr(FthreadCount) + '.txt') then 
    Append(f) 
else 
    Rewrite(f); 
    try 
    i := 0; 
    while i <= 1000000 do 
     Inc(i); 
    Writeln(f, 'done '+floattostr(self.Handle)); 
    finally 
    CloseFile(f); 
    end; 
end; 

{ TDataModule1 } 

procedure TDataModule1.CreateThread; 
var 
    aThrd    : TTestThread; 
begin 
    aThrd := TTestThread.Create(True); 
    aThrd.FreeOnTerminate := True; 
    EnterCriticalSection(fcritical); 
    Inc(FthreadCount); 
    LeaveCriticalSection(fcritical); 
    aThrd.OnTerminate:=Onterminatethrd; 
    try 
    aThrd.Resume; 
    except 
    FreeAndNil(aThrd); 
    end; 
end; 

procedure TDataModule1.Onterminatethrd(Sender: TObject); 
begin 
    EnterCriticalSection(fcritical); 
    Dec(FthreadCount); 
    LeaveCriticalSection(fcritical); 
end; 

procedure TDataModule1.DataModuleCreate(Sender: TObject); 
begin 
    InitializeCriticalSection(fcritical); 
end; 

procedure TDataModule1.DataModuleDestroy(Sender: TObject); 
begin 
    DeleteCriticalSection(fcritical); 
end; 

procedure TDataModule1.execute; 
var 
    i     : integer; 
begin 
    i := 0; 
try 
    while i < 1000 do 
    begin 
    while (FthreadCount = 10) do 
    begin 
     Application.ProcessMessages; 
     CheckSynchronize 
    end; 
    CreateThread; 
    Inc(i); 
    end; 
    while FthreadCount > 0 do 
    begin 
     Application.ProcessMessages; 
     CheckSynchronize; 
    end; 
except on e:Exception do 
// 
end; 
end; 

end. 

en este momento he probado este código varias veces y parece funcionar bien. si Rob me responde con un pequeño ejemplo de cómo puedo implementar semáforos sobre este problema, también publicaré aquí el código completo.

+0

Hay un artículo y ejemplo sobre la implementación de semáforos con Delphi aquí: http://edn.embarcadero.com/article/29908 – Mick

Cuestiones relacionadas