Delphi D10.X 并行库PPL编程之 TParallel.For

Delphi D10.X 并行库PPL编程系列之 TParallel.For

delphi中的RTL(运行库)提供了并行编程库(PPL --Parallel Programming Library) ,让您的应用程序可以在跨平台应用中有效的使用多个CPU并行运行任务的能力。
使用TParallel.For 使循环更快。

TParallel.For说明

TParallel.For通过指定的整数索引值进行迭代,对每次迭代都调用事件处理程序,在并行线程中执行这些调用的程序。迭代器事件是否在并行线程中处理取决于可用的线程资源和性能。
为了表示他与常规的For是一样的功能,TParallel.For同样也使用了For的名称,但TParallel.For使用的是并行执行方式,而不象常规for循环一样一个接一个地串行执行。

TParallel.For重载方法

为了适应不同的需要,TParallel.For提供了很多的重载方法,虽然每个TParallel.For重载方法本质上执行相同的操作,但每个重载方法因参数不同,在执行期间提供功能与信息也是不同的。

以下是不同的重载方法:

class function &For(Sender: TObject; ALowInclusive, AHighInclusive: Integer; AIteratorEvent: TIteratorEvent): TLoopResult; overload; static; inline;
class function &For(Sender: TObject; ALowInclusive, AHighInclusive: Integer; AIteratorEvent: TIteratorEvent; APool: TThreadPool): TLoopResult; overload; static; inline;
class function &For(Sender: TObject; ALowInclusive, AHighInclusive: Integer; AIteratorEvent: TIteratorStateEvent): TLoopResult; overload; static; inline;
class function &For(Sender: TObject; ALowInclusive, AHighInclusive: Integer; AIteratorEvent: TIteratorStateEvent; APool: TThreadPool): TLoopResult; overload; static; inline;
class function &For(Sender: TObject; AStride, ALowInclusive, AHighInclusive: Integer; AIteratorEvent: TIteratorEvent): TLoopResult; overload; static; inline;
class function &For(Sender: TObject; AStride, ALowInclusive, AHighInclusive: Integer; AIteratorEvent: TIteratorEvent; APool: TThreadPool): TLoopResult; overload; static; inline;
class function &For(Sender: TObject; AStride, ALowInclusive, AHighInclusive: Integer; AIteratorEvent: TIteratorStateEvent): TLoopResult; overload; static; inline;
class function &For(Sender: TObject; AStride, ALowInclusive, AHighInclusive: Integer; AIteratorEvent: TIteratorStateEvent; APool: TThreadPool): TLoopResult; overload; static; inline;
class function &For(ALowInclusive, AHighInclusive: Integer; const AIteratorEvent: TProc<Integer>): TLoopResult; overload; static; inline;
class function &For(ALowInclusive, AHighInclusive: Integer; const AIteratorEvent: TProc<Integer>; APool: TThreadPool): TLoopResult; overload; static; inline;
class function &For(ALowInclusive, AHighInclusive: Integer; const AIteratorEvent: TProc<Integer, TLoopState>): TLoopResult; overload; static; inline;
class function &For(ALowInclusive, AHighInclusive: Integer; const AIteratorEvent: TProc<Integer, TLoopState>; APool: TThreadPool): TLoopResult; overload; static; inline;
class function &For(AStride, ALowInclusive, AHighInclusive: Integer; const AIteratorEvent: TProc<Integer>): TLoopResult; overload; static; inline;
class function &For(AStride, ALowInclusive, AHighInclusive: Integer; const AIteratorEvent: TProc<Integer>; APool: TThreadPool): TLoopResult; overload; static; inline;
class function &For(AStride, ALowInclusive, AHighInclusive: Integer; const AIteratorEvent: TProc<Integer, TLoopState>): TLoopResult; overload; static; inline;
class function &For(AStride, ALowInclusive, AHighInclusive: Integer; const AIteratorEvent: TProc<Integer, TLoopState>; APool: TThreadPool): TLoopResult; overload; static; inline;
class function &For(Sender: TObject; ALowInclusive, AHighInclusive: Int64; AIteratorEvent: TIteratorEvent64): TLoopResult; overload; static; inline;
class function &For(Sender: TObject; ALowInclusive, AHighInclusive: Int64; AIteratorEvent: TIteratorEvent64; APool: TThreadPool): TLoopResult; overload; static; inline;
class function &For(Sender: TObject; ALowInclusive, AHighInclusive: Int64; AIteratorEvent: TIteratorStateEvent64): TLoopResult; overload; static; inline;
class function &For(Sender: TObject; ALowInclusive, AHighInclusive: Int64; AIteratorEvent: TIteratorStateEvent64; APool: TThreadPool): TLoopResult; overload; static; inline;
class function &For(Sender: TObject; AStride, ALowInclusive, AHighInclusive: Int64; AIteratorEvent: TIteratorEvent64): TLoopResult; overload; static; inline;
class function &For(Sender: TObject; AStride, ALowInclusive, AHighInclusive: Int64; AIteratorEvent: TIteratorEvent64; APool: TThreadPool): TLoopResult; overload; static; inline;
class function &For(Sender: TObject; AStride, ALowInclusive, AHighInclusive: Int64; AIteratorEvent: TIteratorStateEvent64): TLoopResult; overload; static; inline;
class function &For(Sender: TObject; AStride, ALowInclusive, AHighInclusive: Int64; AIteratorEvent: TIteratorStateEvent64; APool: TThreadPool): TLoopResult; overload; static; inline;
class function &For(ALowInclusive, AHighInclusive: Int64; const AIteratorEvent: TProc<Int64>): TLoopResult; overload; static; inline;
class function &For(ALowInclusive, AHighInclusive: Int64; const AIteratorEvent: TProc<Int64>; APool: TThreadPool): TLoopResult; overload; static; inline;
class function &For(ALowInclusive, AHighInclusive: Int64; const AIteratorEvent: TProc<Int64, TLoopState>): TLoopResult; overload; static; inline;
class function &For(ALowInclusive, AHighInclusive: Int64; const AIteratorEvent: TProc<Int64, TLoopState>; APool: TThreadPool): TLoopResult; overload; static; inline;
class function &For(AStride, ALowInclusive, AHighInclusive: Int64; const AIteratorEvent: TProc<Int64>): TLoopResult; overload; static; inline;
class function &For(AStride, ALowInclusive, AHighInclusive: Int64; const AIteratorEvent: TProc<Int64>; APool: TThreadPool): TLoopResult; overload; static; inline;
class function &For(AStride, ALowInclusive, AHighInclusive: Int64; const AIteratorEvent: TProc<Int64, TLoopState>): TLoopResult; overload; static; inline;
class function &For(AStride, ALowInclusive, AHighInclusive: Int64; const AIteratorEvent: TProc<Int64, TLoopState>; APool: TThreadPool): TLoopResult; overload; static; inline;

可以看到,TParallel.For的所有重载都会返回TParallel.TLoopResult。在执行完TParallel.For iterator事件的所有迭代后,TParallel.For返回的TLoopResult中,会将 Completed 设置为True。
TLoopResult 定义如下:

    TLoopResult = record
    private
      FCompleted: Boolean;
      FLowestBreakIteration: Variant;
    public
      property Completed: Boolean read FCompleted;
      property LowestBreakIteration: Variant read FLowestBreakIteration;
    end;

在使用具有Sender参数的TParallel.For重载时,需要调用者传递一个对象实例,这个对象实例将在每次迭代中作为其Sender参数发送给迭代事件,这与RAD Studio中的其他事件处理程序一样。对于不需要Sender参数参与到事件过程中的,可以将Sender设置为nil传递给迭代事件。

TParallel.For的所有重载方法都涉及参数ALowInclusive和AHighInclusive,它们是Integer或Int64值,分别表示迭代的上下边界。也就是说,如果ALowInclusive的值设置为1、AHighInclusive设置为4,则迭代器事件将在1和4之间执行。
AStride参数允许您调整并行运行的性能。TParallel.For循环使用线程池来管理与执行工作。如果需要执行的工作很少,使用过多的线程反而可能会影响其最终性能。如果将AStride参数设置为大于1,然后根据AStride为每项工作进行分组。这减少这个数值,会减少同步开销上的时间,但代价是减少了循环中的并行性。

迭代事件处理程序的区别在于迭代范围Integer或Int64的大小,以及是否存在TParallel.TLoopState,可以参看下面涉及Sender参数方法的类型定义。

  TIteratorEvent = procedure (Sender: TObject; AIndex: Integer) of object;
  TIteratorStateEvent = procedure (Sender: TObject; AIndex: Integer; const LoopState: TLoopState) of object;
  TIteratorEvent64 = procedure (Sender: TObject; AIndex: Int64) of object;
  TIteratorStateEvent64 = procedure (Sender: TObject; AIndex: Int64; const LoopState: TLoopState) of object;

不是所有迭代事件处理程序都有Sender参数,你也可以使用不带Sender参数的方法,只是他们并没定义为类型。例如:

 const AIteratorEvent: TProc<Int64>

当APool参数指定TThreadPool时,可以通过TThreadPool.SetMinWorkerThreads和ThreadPool.SetMaxWorkerThreads方法来控制迭代事件可用的线程资源。
在使用这些方法时,过多的并发线程会产生开销,从而会减少或消除并行执行程序的优势。

TParallel.For演示

演示中使用到的控件

在这里插入图片描述
使用两个按钮分别执行常规For循环及TParallel.For,通过Memo显示执行结果。

添加使用单元

uses
  System.Threading, // 需要引用PPL库
  System.Diagnostics,
  System.SyncObjs;

先建立一个计算函数

//演示用的执行计算的函数
function IsPrime(N: Integer): Boolean;
var
  Test, k: Integer;
begin
  if N <= 3 then
    IsPrime := N > 1
  else if ((N mod 2) = 0) or ((N mod 3) = 0) then
    IsPrime := False
  else
  begin
    IsPrime := True;
    k := Trunc(Sqrt(N));
    Test := 5;
    while Test <= k do
    begin
      if ((N mod Test) = 0) or ((N mod (Test + 2)) = 0) then
      begin
        IsPrime := False;
        break; { 跳出for循环 }
      end;
      Test := Test + 6;
    end;
  end;
end;

设置一个常数

const
  Max =5000000;

分别为两个按钮增加处理事件

procedure TForm5.Button2Click(Sender: TObject);
var
  I, Tot: Integer;
  SW: TStopwatch;
begin
    // 计算低于给定值的质数
   Tot:=0;
   SW := TStopWatch.Create;
   SW.Start;
   for I := 1 to Max do
   begin
     if IsPrime(I) then
        Inc(Tot);
   end;
   SW.Stop;
   Memo1.Lines.Add(Format('顺序For循环。 时间(以毫秒为单位):%d - 找到质数:%d', [SW.ElapsedMilliseconds,Tot]));
end;

procedure TForm5.Button3Click(Sender: TObject);
var
  Tot: Integer;
  SW: TStopwatch;
begin
     try
     // 计算低于给定值的质数
       Tot :=0;
       SW :=TStopWatch.Create;
       SW.Start;
       TParallel.For(2,1,Max,procedure(I:Int64)
       begin
         if IsPrime(I) then
          TInterlocked.Increment(Tot);
       end);
     SW.Stop;
      Memo1.Lines.Add(Format('并行循环。 时间(以毫秒为单位):%d - 找到质数:%d', [SW.ElapsedMilliseconds,Tot]));
     except on E:EAggregateException do
      ShowMessage(E.ToString);
     end;
end;

演示效果

在这里插入图片描述
使用并行循环明显提高了程序的运行效率。

具体使用请参阅

使用并行编程库
并行库PPL编程之 TTask
并行库PPL编程之 Futures

演示Demo

可下载本系列文章对应的演示程序,含代码。使用D10.3.2编辑。
Delphi D10.X 使用并行编程库使用演示


欢迎光顾本人小店:(https://shop63778938.taobao.com/)
小店也提供delphi方面其他技术支持、定制开发。

现在就进店看看

以上信息对您有用的话请点赞收藏,就下面这行

发布了29 篇原创文章 · 获赞 44 · 访问量 6万+

猜你喜欢

转载自blog.csdn.net/tanqth/article/details/104552520