並列プログラミング ライブラリの TTask の使用
並列プログラミング ライブラリの使用 への移動
並列プログラミング ライブラリ(PPL)には、1 つまたは複数のタスクを並列に実行するための TTask クラスが用意されています。タスクは、完了すべき作業の単位です。PPL では、タスクとそのタスクを実行するスレッドの関連付けを行うので、独自のカスタム スレッドを作成し管理しなくても、複数のタスクを並列に実行することができます。
TTask では、ITask のインスタンスとのやり取りを作成し管理します。ITask は、広範囲な Start、Wait、Cancel へのメソッドやプロパティ、また Status のプロパティ(Created、WaitingToRun、Running、Completed、WaitingForChildren、Canceled、Exception)を提供するインターフェイスです。
バックグラウンドでのタスクの実行
TTask の 1 つの機能は、何らかの処理をバックグラウンドで開始する場合にユーザー インターフェイスが動かなくなるのを避けることです。 次のコード例では、単一のタスクを作成し開始する方法を示しています:
Delphi:
procedure TForm1.Button1Click(Sender: TObject);
var
aTask: ITask;
begin
aTask := TTask.Create(
procedure
begin
sleep (3000); // 3 seconds
TThread.Synchronize(TThread.Current,
procedure
begin
ShowMessage ('Hello');
end);
end);
aTask.Start;
end;
C++:
Clang 拡張 C++ コンパイラを使用している場合、ラムダ式を使用することができます:
void __fastcall TForm1::Button1Click(TObject *Sender)
{
_di_ITask task = TTask::Create([&](){
Sleep(3000);
ShowMessage("Hello");
});
task->Start();
}
C++ コンパイラの場合、TProc サブクラスを宣言し、そのインスタンスを _di_TProc
としてキャストして使用します:
class TCppTask : public TCppInterfacedObject<TProc> {
public:
void __fastcall Invoke() {
Sleep(3000);
ShowMessage("Hello");
}
};
void __fastcall TForm1::Button1Click(TObject *Sender)
{
_di_ITask task = TTask::Create(_di_TProc(new TCppTask()));
task->Start();
}
タスクが完了するのを待機する
TTask には、すべてまたは任意のタスクが完了するのを待つための、WaitForAll や WaitForAny が用意されています。WaitForAll が、すべてのタスクの完了時に制御を返すのに対して、WaitForAny は最初に完了したタスクを通知します。たとえば、2 つのタスク A と B があり、それぞれ 3 秒と 5 秒かかる場合、結果が得られるまでの時間は次のとおりです:
- 順次処理(TTask/ITask を使用しない場合) = 8 秒
- TTask.WaitForAny = 3 秒
- TTask.WaitForAll = 5 秒
次の例では、WaitForAll メソッドを使用しています:
Delphi:
procedure TForm1.MyButtonClick(Sender: TObject);
var
tasks: array of ITask;
value: Integer;
begin
Setlength (tasks ,2);
value := 0;
tasks[0] := TTask.Create (procedure ()
begin
sleep(3000);
TInterlocked.Add (value, 3000);
end);
tasks[0].Start;
tasks[1] := TTask.Create (procedure ()
begin
sleep (5000);
TInterlocked.Add (value, 5000);
end);
tasks[1].Start;
TTask.WaitForAll(tasks);
ShowMessage ('All done: ' + value.ToString);
end;
C++:
Clang 拡張 C++ コンパイラを使用している場合、ラムダ式を使用することができます:
void __fastcall TForm1::Button1Click(TObject *Sender)
{
_di_ITask tasks[2];
int value = 0;
tasks[0] = TTask::Create([&](){
Sleep(3000);
TInterlocked::Add(value, 3000);
});
tasks[0]->Start();
tasks[1] = TTask::Create([&](){
Sleep(5000);
TInterlocked::Add(value, 5000);
});
tasks[1]->Start();
TTask::WaitForAll(tasks, sizeof(tasks)/sizeof(tasks[0])-1);
ShowMessage("All done! " + IntToStr(value));
}
C++ コンパイラの場合、TProc サブクラスを宣言し、そのインスタンスを _di_TProc
としてキャストして使用します:
class TCppTask : public TCppInterfacedObject<TProc> {
public:
TCppTask(int &value, int increase) : m_value(value), m_increase(increase) {}
void __fastcall Invoke() {
Sleep(m_increase);
TInterlocked::Add(m_value, m_increase);
}
protected:
int& m_value;
int m_increase;
};
void __fastcall TForm1::Button1Click(TObject *Sender)
{
_di_ITask tasks[2];
int value = 0;
tasks[0] = TTask::Create(_di_TProc(new TCppTask(value, 3000)));
tasks[0]->Start();
tasks[1] = TTask::Create(_di_TProc(new TCppTask(value, 5000)));
tasks[1]->Start();
TTask::WaitForAll(tasks,(sizeof(tasks)/sizeof(tasks[0])-1));
ShowMessage("All done! " + IntToStr(value));
}