如何在Delphi中实现并行循环?

如何在Delphi中实现并行循环?,delphi,parallel-processing,delphi-xe,Delphi,Parallel Processing,Delphi Xe,如何在Delphi(delphi200x,delphixe)中实现并行循环?如何做到最好?有没有普遍的解决办法 请举例说明。取决于您所指的并行循环和应用程序/实现 看看TThread和TMultiReadExclusiveWriteSynchronizer。目前最好的解决方案可能是中的并行For循环构造。向它传递一个集合或一对表示上下限的整数,以及一个表示循环体的匿名方法,它使用线程池并行运行for循环 请注意,这仅在循环体方法能够独立运行时才起作用。如果它修改了任何外部变量,或者依赖于在循环中

如何在Delphi(delphi200x,delphixe)中实现并行循环?如何做到最好?有没有普遍的解决办法


请举例说明。

取决于您所指的并行循环和应用程序/实现


看看TThread和TMultiReadExclusiveWriteSynchronizer。

目前最好的解决方案可能是中的并行For循环构造。向它传递一个集合或一对表示上下限的整数,以及一个表示循环体的匿名方法,它使用线程池并行运行for循环

请注意,这仅在循环体方法能够独立运行时才起作用。如果它修改了任何外部变量,或者依赖于在循环中前面进行的计算的值,那么它就不能被并行化

可以找到对OmniThreadLibrary for parallel的介绍。例如,对数字进行迭代的简单For循环如下所示:

Parallel.ForEach(1, testSize).Execute(
  procedure (const elem: integer)
  begin
    // do something with 'elem'
  end);

如果您只需要ParallelFor,则可以使用以下代码:

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;
接口
使用
类,sysutil;
类型
TParallelProc=对过程的引用(i:Integer;ThreadID:Integer);
TParallel=class(TThread)
私有的
FProc:TParallelProc;
FThreadID:整数//当前线程ID
受保护的
程序执行;推翻
函数GetNextValue:整数;
公众的
构造函数创建;
毁灭者毁灭;推翻
属性进程:TParallelProc
读FProc写FProc;
类变量
CurrPos:整数//电流环指数
MaxPos:整数//最大循环指数
cs:TCriticalSection;
ThCount:整数//线程计数器-已完成执行的线程数
结束;
{**ParallelFor循环-所有迭代都将在选定的线程中执行
@参数nMin-循环最小值(第一次迭代)
@param nMax-循环最大值(最后一次迭代)
@param nThreads—要使用多少线程
@param aProc-将在循环线程中执行的匿名过程
}
并行过程(nMin,nMax,nThreads:Integer;aProc:TParallelProc);超载;
{**ParallelFor循环-所有迭代都将在最大cpu内核中执行
@参数nMin-循环最小值(第一次迭代)
@param nMax-循环最大值(最后一次迭代)
@param aProc-将在循环线程中执行的匿名过程
}
并行过程(nMin,nMax:Integer;aProc:TParallelProc);超载;
实施
使用
{$IFDEF MSWINDOWS}
窗户,
{$ENDIF}
SyncObjs;
并行过程(nMin,nMax,nThreads:Integer;aProc:TParallelProc);
变量
线程:Tpallel阵列;
I:整数;
开始
如果nMin>nMax,则
出口
//初始化TParallel类数据
TParallel.CurrPos:=nMin;
TParallel.MaxPos:=nMax;
TParallel.cs:=TCriticalSection.Create;
TParallel.ThCount:=0;
//创建线程
设置长度(螺纹、螺纹);
对于I:=0到-1 do
开始
线程[I]:=TParallel.Create;//暂停的
线程[I].FThreadID:=I;
线程[I].Proc:=aProc;
线程[I]。启动;
结束;
对于I:=0到-1 do
开始
线程[I].WaitFor;
结束;
对于I:=0到-1 do
开始
线程[I]。自由;
结束;
TParallel.cs.Free;
结束;
并行过程(nMin,nMax:Integer;aProc:TParallelProc);
开始
并行计算(nMin、nMax、CPUCount、aProc);
结束;
{TParallel}
构造函数TParallel.Create;
开始
继承的创建(True);//暂停的
联锁增量(ThCount);
FreeOnTerminate:=False;
fthread:=0;
结束;
毁灭者t平行毁灭;
开始
联锁减量(ThCount);
继承;
结束;
程序TParallel.Execute;
变量
n当前:整数;
开始
nCurrent:=GetNextValue;

当我遇到类似于[parallel for I:=1到10做MyProc(I);]或者类似于[ParallelDo(I,1,10,MyProc)]的事情时,你觉得什么这么复杂?这是一个非常简单的解决方案,比使用OmniThreadLibrary简单得多。您可以这样编写:ParallelFor(0,Count-1,过程(i:Integer;ThreadID:Integer)begin{dosomething}end);我认为库解决方案(Omni是明显的选择)有一些优势。首先,本规范没有任何违规行为,存在性能问题。每次调用ParallelFor时创建和销毁线程的成本都很高。最好是有一个游泳池等着出发。在循环中调用WaitFor也是一个错误,在Win32中应该使用一次多个对象等待函数。临界段浪费-联锁增量更好。当任务很小时,这些可能是重要的问题。也没有处理异常,这是一个显著的遗漏。很明显,库解决方案具有优势。我在答复中指出了这一点。但有时你只需要一个最简单的解决方案。就个人而言,我不认为创建和销毁线程是不好的。也许它有点慢(在大多数情况下,你甚至没有注意到这一点),但我不喜欢创建对象,特别是如果你在某些情况下根本不使用它们。您可以在匿名方法中处理异常。@除非您不想被强制在匿名方法中处理异常。当然,它们必须在线程中处理。您想要的是异常返回到主线程。当然,创建/销毁线程对于长时间运行的任务来说是没有问题的,但是对于短时间运行的任务来说,这是一个巨大的开销。与此解决方案相比,OmniThreadLibrary Parallel.ForEach至少有三个优势(我并不是说Linas的解决方案不好)。1) OTL将使用线程池执行线程。2) OTL任务如果提前完成,将从其他任务中窃取工作。3) OTL Parallel.ForEach可以保留输入顺序(不是执行顺序,而是结果顺序)。可以发布使用OmniThreadLibrart实现并行循环的示例吗?或链接到example@Astronavigator:有关示例,请参见最后一段中的链接。