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

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

検索:

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

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

画面設計

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

ソースコードの記述

以下のソースコードを記述します。

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

  //素数の判定結果を入れるクラス
  TAnswers=class(TObject)
  public
    res:TArray<String>;
  end;

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

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


var
  Form1: TForm1;

implementation

{$R *.dfm}

{ TPrimeTest }

constructor TPrimeTest.Create(Num: Int64; Answers: TAnswers);
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;
    finally
      //保護(ロック)を解除する
      System.MonitorExit(fAnswers);
    end;
  end;
end;

{TForm1}

procedure TForm1.Button1Click(Sender: TObject);
var i,n:Int64;
    Tasks:TArray<ITask>;
begin
  Memo1.Lines.Clear;
  SetLength(Answers.res,0);
  //10個の並列処理用タスクを準備する
  SetLength(Tasks,10);

  //10000から50000 までの間の素数を見つける
  n:=10000;
  while n<50000 do
  begin
    Tasks[0]:=TPrimeTest.Create(n,Answers) as ITask;
    inc(n);
    Tasks[1]:=TPrimeTest.Create(n,Answers) as ITask;
    inc(n);
    Tasks[2]:=TPrimeTest.Create(n,Answers) as ITask;
    inc(n);
    Tasks[3]:=TPrimeTest.Create(n,Answers) as ITask;
    inc(n);
    Tasks[4]:=TPrimeTest.Create(n,Answers) as ITask;
    inc(n);
    Tasks[5]:=TPrimeTest.Create(n,Answers) as ITask;
    inc(n);
    Tasks[6]:=TPrimeTest.Create(n,Answers) as ITask;
    inc(n);
    Tasks[7]:=TPrimeTest.Create(n,Answers) as ITask;
    inc(n);
    Tasks[8]:=TPrimeTest.Create(n,Answers) as ITask;
    inc(n);
    Tasks[9]:=TPrimeTest.Create(n,Answers) as ITask;
    inc(n);
    //10個のタスクの並列処理を開始する
    for i := Low(Tasks) to High(Tasks) do
      Tasks[i].Start;
    //全てのタスクが完了するまで待つ
    //10個のタスクが開始する順序と終了する順序は保証されない
    TTask.WaitForAll(Tasks);
  end;

  Memo1.Lines.BeginUpdate;
  for i := Low(Answers.res) to High(Answers.res) do
    Memo1.Lines.Add((Answers.res)[i]);
  Memo1.Lines.EndUpdate;
end;

procedure TForm1.FormCreate(Sender: TObject);
begin
  //回答用クラスのインスタンスを作成する
  Answers:=TAnswers.Create;
end;

procedure TForm1.FormDestroy(Sender: TObject);
begin
  Answers.Free;
end;

end.

実行する

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