Cómo realizar bucles paralelos en Delphi (Delphi 200X, Delphi XE)? ¿Cómo hacer esto de la mejor manera? ¿Y hay alguna solución universal?¿Cómo realizar un bucle paralelo en Delphi?
Con ejemplos, por favor.
Cómo realizar bucles paralelos en Delphi (Delphi 200X, Delphi XE)? ¿Cómo hacer esto de la mejor manera? ¿Y hay alguna solución universal?¿Cómo realizar un bucle paralelo en Delphi?
Con ejemplos, por favor.
Probablemente la mejor solución en este momento es la construcción Parallel For Loop en OmniThreadLibrary. Le pasa una colección o un par de enteros que representan los límites inferior y superior, y un método anónimo que representa el cuerpo del bucle, y utiliza un grupo de subprocesos para ejecutar el bucle for en paralelo.
Tenga en cuenta que esto solo funcionará si el método del cuerpo del bucle es capaz de funcionar por sí mismo. Si modifica cualquier variable externa o se basa en el valor de un cálculo realizado anteriormente en el ciclo, entonces no puede ser paralelizado.
Puede encontrar una introducción al paralelo de OmniThreadLibrary here. Por ejemplo, un simple para la iteración del bucle sobre los números se parece a esto:
Parallel.ForEach(1, testSize).Execute(
procedure (const elem: integer)
begin
// do something with 'elem'
end);
Depende de lo que quiere decir con bucle paralelo y la aplicación/implementación.
Eche un vistazo a TThread y TMultiReadExclusiveWriteSynchronizer.
Meen algo así como [parallel for i: = 1 to 10 do MyProc (i); ] o tal vez algo como [ParallelDo (i, 1,10, MyProc)] – Astronavigator
Si sólo necesita ParallelFor puede utilizar este código:
interface
uses
Classes, SysUtils;
type
TParallelProc = reference to procedure(i: Integer; ThreadID: Integer);
TParallel = class(TThread)
private
FProc: TParallelProc;
FThreadID: Integer; //current thread ID
protected
procedure Execute; override;
function GetNextValue: Integer;
public
constructor Create;
destructor Destroy; override;
property Proc: TParallelProc
read FProc write FProc;
class var
CurrPos: Integer; //current loop index
MaxPos: Integer; //max loops index
cs: TCriticalSection;
ThCount: Integer; //thread counter - how much threads have finished execution
end;
{** ParallelFor Loop - all iterations will be performed in chosen threads
@param nMin - Loop min value (first iteration)
@param nMax - Loop max value (last iteration)
@param nThreads - how much threads to use
@param aProc - anonymous procedure which will be performed in loop thread
}
procedure ParallelFor(nMin, nMax, nThreads: Integer; aProc: TParallelProc); overload;
{** ParallelFor Loop - all iterations will be performed in max cpu cores
@param nMin - Loop min value (first iteration)
@param nMax - Loop max value (last iteration)
@param aProc - anonymous procedure which will be performed in loop thread
}
procedure ParallelFor(nMin, nMax: Integer; aProc: TParallelProc); overload;
implementation
uses
{$IFDEF MSWINDOWS}
Windows,
{$ENDIF}
SyncObjs;
procedure ParallelFor(nMin, nMax, nThreads: Integer; aProc: TParallelProc);
var
threads: array of TParallel;
I: Integer;
begin
if nMin > nMax then
Exit;
// initialize TParallel class data
TParallel.CurrPos := nMin;
TParallel.MaxPos := nMax;
TParallel.cs := TCriticalSection.Create;
TParallel.ThCount := 0;
// create the threads
SetLength (threads, nThreads);
for I := 0 to nThreads - 1 do
begin
threads[I] := TParallel.Create; // suspended
threads[I].FThreadID := I;
threads[I].Proc := aProc;
threads[I].Start;
end;
for I := 0 to nThreads - 1 do
begin
threads[I].WaitFor;
end;
for I := 0 to nThreads - 1 do
begin
threads[I].Free;
end;
TParallel.cs.Free;
end;
procedure ParallelFor(nMin, nMax: Integer; aProc: TParallelProc);
begin
ParallelFor(nMin, nMax, CPUCount, aProc);
end;
{ TParallel }
constructor TParallel.Create;
begin
inherited Create(True); // suspended
InterlockedIncrement(ThCount);
FreeOnTerminate := False;
FThreadID := 0;
end;
destructor TParallel.Destroy;
begin
InterlockedDecrement(ThCount);
inherited;
end;
procedure TParallel.Execute;
var
nCurrent: Integer;
begin
nCurrent := GetNextValue;
while nCurrent <= MaxPos do
begin
Proc(nCurrent, FThreadID);
nCurrent := GetNextValue;
end;
end;
function TParallel.GetNextValue: Integer;
begin
cs.Acquire;
try
Result := CurrPos;
Inc(CurrPos);
finally
cs.Release;
end;
end;
Sin embargo, si necesita más enhebrado "cosas" que debería considerar usar bibliotecas de terceros.
Solución bastante complicada ... – Astronavigator
¿Qué es tan complicado para usted? Es una solución muy simple IMO. Mucho más simple que usar OmniThreadLibrary. Puede escribir algo como esto: ParaleloPara (0, Count - 1, procedimiento (i: Integer; ThreadID: Integer) begin {do something} end); – Linas
Creo que una solución de biblioteca (Omni es la elección obvia) tiene algunas ventajas. Para empezar, este código, sin intención de ofender, tiene problemas de rendimiento. Es costoso crear y destruir subprocesos cada vez que llamas a ParallelFor. Mejor es tener un grupo esperando listo para ir. También es un error llamar a WaitFor en un bucle, debería usar una de las múltiples funciones de espera de objetos en Win32. La sección crítica es derrochadora: el incremento entrelazado es mejor. Estos pueden ser problemas importantes cuando las tareas son pequeñas. Tampoco se manejan las excepciones, lo cual es una omisión significativa. –
Esto parece genial. –
¿Podría publicar un ejemplo de realización de bucles paralelos utilizando OmniThreadLibrart? O enlace al ejemplo – Astronavigator
@Astronavigator: consulte el enlace en el último párrafo para ver ejemplos. –