トップへ(mam-mam.net/)

TTaskクラスを使って並列プログラミング ~Delphiソースコード集

English

TTaskクラスを使って並列プログラミング ~Delphiソースコード集

DelphiでTTaskクラスを使って並列プログラミングする方法を、素数判定のサンプルソースコードで解説します。
このサンプルソースコードでは100個のTTaskを使って100並列で素数判定を行います。

画面設計

Delphi IDEを起動し、「ファイル」⇒「Windows VCLアプリケーション -Delphi」をクリックしてプロジェクトと初期フォームを作成します。
右下ペインにある「パレット」から以下コンポーネントをドラッグ&ドロップします。

ソースコードの記述

F12を押して コードエディタ に切り替えて以下のソースコードをコピペで記述します。
F12を押して フォームデザイナ に切り替えて、左下ペインのオブジェクトインスペクタで[イベント]タブを選択、Button1をクリックして選択し、 Button1.OnCreate に Button1Click を設定します。

unit Unit1;

interface

uses
  Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
  Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls,
  System.Threading, System.SyncObjs, System.Math;

type

  //並列プログラミング ライブラリの TTask を使用して
  //Numが素数の場合はfAnswersに追加する
  TPrimeTest=class(TTask, ITask)
  private
    fNum:INT64;
    fAnswers:TStringList;
  public
    constructor Create(Num:Int64; Answers:TStringList);
    procedure TaskStart;
  end;


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


var
  Form1: TForm1;

implementation

{$R *.dfm}

{ TPrimeTest }

constructor TPrimeTest.Create(Num: Int64; Answers: TStringList);
begin
  //プライベート変数に保存する
  fNum:=Num;
  fAnswers:=Answers;
  //親クラスのCreateを呼び出す
  inherited Create(
    nil, nil,
    procedure
    begin
      Self.TaskStart;
    end,
    nil, nil, []
  );
end;

procedure TPrimeTest.TaskStart;
var i,m:Int64;
    flag:Boolean;
    st:String;
begin
  st:='';
  if fNum<2 then
  begin
    exit;
  end
  else if (fNum=2) or (fNum=3) then
  begin
    st:=Format('%d は素数',[fNum]);
  end
  else
  begin
    m:=Trunc(Sqrt(fNum));
    i:=2;
    flag:=True;
    while i<=m do
    begin
      if fNum Mod i =0 then
      begin
        flag:=False; //素数ではない
        break;
      end;
      inc(i);
    end;
    if flag then
      st:=Format('%d は素数',[fNum]);
  end;

  if st<>'' then
  begin
    //同時にオブジェクト使用しないように保護(ロック)する
    System.MonitorEnter(fAnswers);
    try
      //配列を1個増やす
      //SetLength(fAnswers.res,Length(fAnswers.res)+1);
      //fAnswers.res[Length(fAnswers.res)-1]:=st;
      fAnswers.Add(st);
    finally
      //保護(ロック)を解除する
      System.MonitorExit(fAnswers);
    end;
  end;
end;

{TForm1}

procedure TForm1.Button1Click(Sender: TObject);
const
    //最大100個のタスクを並列実行させる
    MaxTaskCount:Integer = 100;
    //10000から50000 の間にある素数を見つける
    FromNumber:Integer=10000;
    ToNumber  :Integer=50000;
var i,n:Int64;
    Tasks:TArray<ITask>;
    TaskCount:Integer;
    Answers:TStringList;
begin
  Memo1.ScrollBars:=ssBoth;
  Answers:=TStringList.Create;
  try
    n:=FromNumber;
    while n<=ToNumber do
    begin

      if (n+MaxTaskCount-1)>ToNumber then
        TaskCount:=ToNumber-n+1
      else
        TaskCount:=MaxTaskCount;

      //並列処理用タスクを準備する
      SetLength(Tasks,TaskCount);
      for i := Low(Tasks) to High(Tasks) do
      begin
        Tasks[i]:=TPrimeTest.Create(n,Answers) as ITask;
        inc(n);
      end;

      //全てのタスクの並列処理を開始する
      for i := Low(Tasks) to High(Tasks) do Tasks[i].Start;

      //全てのタスクが完了するまで待つ
      //タスクが開始する順序と終了する順序は保証されない
      TTask.WaitForAll(Tasks);
    end;

    //Memo1.Lines.BeginUpdate;
    Memo1.Lines.Assign(Answers);
    //Memo1.Lines.EndUpdate;
  finally
    Answers.Free;
  end;
end;

end.

実行する

IDEの実行ボタンをクリックして実行します。
Button1をクリックすると、10000から50000までの間にある素数をMemo1に表示します。