チュートリアル:並列プログラミング ライブラリのタスクを使用する
並列プログラミング ライブラリ関連のチュートリアル への移動
目次
このチュートリアルでは、並列プログラミング ライブラリ(PPL)のタスクを使ってアプリケーションを実装する方法を説明します。タスクとは、キューに入れられ、CPU 時間が利用できるようになると開始される、作業の単位です。タスクを使用することで、操作を並行して実行できます。1 つ存在するマスタ スレッドが、キューを管理し、スレッド プールのスレッドを割り当ててタスクの作業を行わせます。このスレッド プールには複数のスレッドが含まれていますが、そのスレッド数は利用可能な CPU の数によって変わります。
プロジェクトの作成
新規プロジェクトを作成します。
- Delphi の場合は、[ファイル|新規作成|マルチデバイス アプリケーション - Delphi|空のアプリケーション]を選択します。
- C++Builder の場合、[ファイル|新規作成|マルチデバイス アプリケーション - C++Builder|空のアプリケーション]を選択します。
コンポーネントの追加
- フォームに TButton コンポーネントと TLabel コンポーネントを追加します。TButton はタスクを開始するためのもの、TLabel は結果を表示するためのものです。
- フォームに TScrollBar コンポーネントをドロップします。
- [オブジェクト インスペクタ]で、スクロール バーの Align プロパティを
Top
に設定します。
- [オブジェクト インスペクタ]で、スクロール バーの Align プロパティを
- フォームに TFloatAnimation をドロップします。
- スクロールバーの下に TFloatAnimation を入れ子にします。コンポーネントの構造は次のようになります:
- AutoReverse プロパティを
True
に設定します。 - Duration プロパティを
1
秒に設定します。 - Enabled プロパティを
True
に設定します。 - Loop プロパティを
True
に設定します。 - PropertyName プロパティを
Value
に設定します。 - StartValue プロパティを
0
に、StopValue プロパティを100
に設定します。
コンポーネントのイベント ハンドラの実装
まず、TButton の OnClick イベントのイベント ハンドラを実装します。次のコードを記述します。
procedure TForm1.Button1Click(Sender: TObject);
var
lValue: Integer;
begin
Label1.Text := '--';
{Some calculation that takes time}
Sleep(3000);
lValue := Random(10);
Label1.Text := lValue.ToString;
end;
end.
void __fastcall TForm1::Button1Click(TObject *Sender)
{
int lValue;
Label1->Text = "--";
//{Some calculation that takes time}
Sleep(3000);
lValue = Random(10);
Label1->Text = String(lValue);
}
このコードは、ボタンを押すと開始する、時間のかかる計算処理の例となっています。3 秒間スリープし、その後、0 ~ 10 の間のランダムな数値を表示します。
PPL 実装なしでのアプリケーションの実行
この時点で、アプリケーションを実行することができます。
F9
キーを押すか、[実行|実行]を選択します。
スクロール バーが左から右へ移動していくのがわかります。ただし、ボタンを押して時間のかかる計算が始まると、計算が終わるまでスクロール バーは停止します。 これはもちろん、期待される動作ではありません。そのため、次のセクションでは、PPL の TTask 機能を使用してこれを改善する方法を説明します。
TTask 機能の実装
このセクションでは、時間のかかる計算(OnClick イベントとして実装されたもの)を TTask に埋め込んで、この処理を並列に実行する方法を説明します。
まず、先ほどのアプリケーション フォームを使用して、TButton コンポーネントの名前を「ButtonTask1」に、TLabel コンポーネントの名前を「LabelTask1」に変更します。
次に、PPL ライブラリをプロジェクトに含めます。
uses
System.Threading;
#include <System.Threading.hpp>
計算処理をタスクとして実行できるよう、PPL には TTask.Run メソッドが用意されています。コードを次のように変更します。
procedure TForm1.ButtonTask1Click(Sender: TObject);
var
lValue: Integer;
begin
Label1.Text := '--';
TTask.Run(procedure
begin
{Some calculation that takes time}
Sleep(3000);
lValue := Random(10);
TThread.Synchronize(nil,
procedure
begin
Label1.Text := lValue.ToString;
end);
end);
end;
Delphi の TTask.Run メソッドで使用できる入力パラメータ オプションはいくつかありますが、このコードでは、無名メソッドを使用して時間のかかる計算を行っています。
TCppSync
クラスおよび TCppTask
クラスのコードも追加します。
class TCppSync : public TCppInterfacedObject<TThreadProcedure> {
int &lValue;
TLabel *Label;
public:
TCppSync(int &l, TLabel *lbl):lValue(l),Label(lbl)
{}
void __fastcall Invoke(){
Label->Text = String(lValue);
}
};
class TCppTask : public TCppInterfacedObject<TProc> {
int &lValue;
TLabel *Label;
public:
TCppTask(int &l, TLabel *lbl): lValue(l), Label(lbl)
{}
void __fastcall Invoke(){
Sleep(3000);
lValue = Random(10);
TThread::Synchronize(0,_di_TThreadProcedure(new TCppSync(lValue, Label)));
}
};
void __fastcall TForm1::ButtonTask1Click(TObject *Sender)
{
LabelTask1->Text = "--";
TTask::Run(_di_TProc(new TCppTask(lValue, LabelTask1)));
}
ヘッダー ファイルの private セクションに lValue
の宣言を追加します。
private: // User declarations
int lValue;
C++ の TTask.Run メソッドで使用できる入力パラメータ オプションはいくつかありますが、このコードでは TProc 型を使用して時間のかかる計算を行っています。
TTask 機能の実装の実行
この時点で、アプリケーションを実行することができます。
F9
キーを押すか、[実行|実行]を選択します。
今回は、ButtonTask1 を押しても、スクロール バーの動作は継続し、中断されません。つまり、PPL の TTask 機能を使用すると、複数のタスクを並列で実行することができます。