チュートリアル:並列プログラミング ライブラリのタスクを使用する

提供: RAD Studio
移動先: 案内検索

並列プログラミング ライブラリ関連のチュートリアル への移動

このチュートリアルでは、並列プログラミング ライブラリ(PPL)タスクを使ってアプリケーションを実装する方法を説明します。タスクとは、キューに入れられ、CPU 時間が利用できるようになると開始される、作業の単位です。タスクを使用することで、操作を並行して実行できます。1 つ存在するマスタ スレッドが、キューを管理し、スレッド プールのスレッドを割り当ててタスクの作業を行わせます。このスレッド プールには複数のスレッドが含まれていますが、そのスレッド数は利用可能な CPU の数によって変わります。

プロジェクトの作成

新規プロジェクトを作成します。

  • Delphi の場合は、[ファイル|新規作成|マルチデバイス アプリケーション - Delphi|空のアプリケーション]を選択します。
  • C++Builder の場合、[ファイル|新規作成|マルチデバイス アプリケーション - C++Builder|空のアプリケーション]を選択します。

コンポーネントの追加

  1. フォームに TButton コンポーネントと TLabel コンポーネントを追加します。TButton はタスクを開始するためのもの、TLabel は結果を表示するためのものです。
  2. フォームに TScrollBar コンポーネントをドロップします。
  3. フォームに TFloatAnimation をドロップします。
    • スクロールバーの下に TFloatAnimation を入れ子にします。コンポーネントの構造は次のようになります:
    Structure task.png
    • AutoReverse プロパティを True に設定します。
    • Duration プロパティを 1 秒に設定します。
    • Enabled プロパティを True に設定します。
    • Loop プロパティを True に設定します。
    • PropertyName プロパティを Value に設定します。
    • StartValue プロパティを 0 に、StopValue プロパティを 100 に設定します。

コンポーネントのイベント ハンドラの実装

まず、TButtonOnClick イベントのイベント ハンドラを実装します。次のコードを記述します。

Delphi の場合:
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.
C++ の場合:
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 キーを押すか、[実行|実行を選択します。
Run1.png

スクロール バーが左から右へ移動していくのがわかります。ただし、ボタンを押して時間のかかる計算が始まると、計算が終わるまでスクロール バーは停止します。 これはもちろん、期待される動作ではありません。そのため、次のセクションでは、PPLTTask 機能を使用してこれを改善する方法を説明します。

TTask 機能の実装

このセクションでは、時間のかかる計算(OnClick イベントとして実装されたもの)を TTask に埋め込んで、この処理を並列に実行する方法を説明します。

まず、先ほどのアプリケーション フォームを使用して、TButton コンポーネントの名前を「ButtonTask1」に、TLabel コンポーネントの名前を「LabelTask1」に変更します。

次に、PPL ライブラリをプロジェクトに含めます。

Delphi アプリケーションの場合、次のユニットが uses 句に含まれていなければ追加します。
uses
  System.Threading;
C++ アプリケーションの場合、プロジェクト ヘッダー ファイルに以下の #include を追加します。
#include <System.Threading.hpp>

計算処理をタスクとして実行できるよう、PPL には TTask.Run メソッドが用意されています。コードを次のように変更します。

Delphi の場合:
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 メソッドで使用できる入力パラメータ オプションはいくつかありますが、このコードでは、無名メソッドを使用して時間のかかる計算を行っています。

C++ の場合:

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 を押しても、スクロール バーの動作は継続し、中断されません。つまり、PPLTTask 機能を使用すると、複数のタスクを並列で実行することができます。

関連項目