ParallelFor

出来ましたよと. ブロック内の計算量が少ない場合、ループの中で関数コールするオーバーヘッドが大きくなるので、2引数の無名関数にも対応.

unit ParallelUtils;

interface
uses Windows, SysUtils, Forms;

procedure ParallelFor(Start, Stop: Integer; Block: TProc<Integer, Integer>); overload;
procedure ParallelFor(Start, Stop: Integer; Block: TProc<Integer>); overload;

implementation

type
  TThreadInfo = record
    Start: Integer;
    Stop: Integer;
    Proc: TProc<Integer, Integer>;
    ThreadId: LongWord;
    ThreadHandle: Integer;
  end;
  PThreadInfo = ^TThreadInfo;

function GetNumberOfProcessors: Integer;
var
  SystemInfo: TSystemInfo;
begin
  GetSystemInfo(SystemInfo);
  Result := SystemInfo.dwNumberOfProcessors;
end;

function PallarelForThreadFunc(Parameter: Pointer): Integer;
begin
  Result := 0;
  with PThreadInfo(Parameter)^ do
    Proc(Start, Stop);
  EndThread(0);
end;

procedure ParallelFor(Start, Stop: Integer; Block: TProc<Integer, Integer>); overload;
var
  I: Integer;
  ThreadCount: Integer;
  Infos: array of TThreadInfo;
begin
  ThreadCount := GetNumberOfProcessors;
  SetLength(Infos, ThreadCount);
  for I := 0 to ThreadCount - 1 do
  begin
    Infos[I].Start := Start + (Stop - Start + 1) * I div ThreadCount;
    Infos[I].Stop := Start + (Stop - Start + 1) * (I + 1) div ThreadCount - 1;
    Infos[I].Proc := Block;
    Infos[I].ThreadHandle := BeginThread(nil, 0, PallarelForThreadFunc, @Infos[I], 0, Infos[I].ThreadId);
  end;
  for I := 0 to ThreadCount - 1 do
  begin
    while WaitForSingleObject(Infos[I].ThreadHandle, 0) = WAIT_TIMEOUT do
    begin
      Application.ProcessMessages;
      Sleep(1);
    end;
    CloseHandle(Infos[I].ThreadHandle);
  end;
end;

procedure ParallelFor(Start, Stop: Integer; Block: TProc<Integer>); overload;
begin
  ParallelFor(Start, Stop,
    procedure (Start, Stop: Integer)
    var
      I: Integer;
    begin
      for I := Start to Stop do Block(I);
    end);
end;

end.

ではテスト.

unit Unit1;

interface

uses
  SysUtils, Classes, Controls, Forms, StdCtrls, ParallelUtils;

type
  TForm1 = class(TForm)
    Button1: TButton;
    Memo1: TMemo;
    procedure Button1Click(Sender: TObject);
  private
    { Private 宣言 }
  public
    { Public 宣言 }
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

procedure TForm1.Button1Click(Sender: TObject);
var
  FreeVariable: Integer;
begin
  FreeVariable := 10;
  ParallelFor(0, 9,
    procedure (I: Integer)
    begin
      MonitorEnter(Memo1);
      try
        Memo1.Lines.Add(IntToStr(I + FreeVariable));
      finally
        MonitorExit(Memo1);
      end;
    end);
  ParallelFor(10, 19,
    procedure (Start, Stop: Integer)
    var
      I: Integer;
    begin
      MonitorEnter(Memo1);
      try
        for I := Start to Stop do
          Memo1.Lines.Add(IntToStr(I + FreeVariable));
      finally
        MonitorExit(Memo1);
      end;
    end);
end;

end.

結果は以下の通りに.

Memo1
10
15
11
16
12
17
13
18
14
19
20
21
22
23
24
25
26
27
28
29