Delphi Knowledge 並列プログラミング タスク

Delphi XE7 以降、並列プログラミング ライブラリParallel Programming Library ( PPL ) がサポートされています。では、PPLとは何でしょうか? PPL はDelphi RTL の一部であり、マルチスレッド (または並列) プログラミングに非常に便利です。

PPLは、Delphi がサポートするすべてのプラットフォームに適用でき、タスクの実行、タスクの接続、タスクの実行の待機などの高度な機能を提供します。PPLはスレッドとは異なります。PPLスレッド プールをサポートし、CPU ベースの負荷を自動的に管理できるため、開発者はこれらのスレッドを考慮して作成する必要がありません。

Delphi の PPL の使用は非常に簡単で、アプリケーションに大きな変更を加える必要はありません。System.Threading ユニットをプログラムに追加すると、このライブラリを作成できます。

問題は、いつ、どのように PPL を使用できるかということです。通常、タスクを考慮すると、スレッドを作成しなくなるわけではありませんが、多くの場合、通常のスレッドの代わりにソートされたタスクを使用することになります。

単純な PPL を使用した一般的なコード例は次のとおりです。

procedure TMainForm.btnITaskClick(Sender: TObject);
var
  LTask: ITask;
begin
  LTask := TTask.Run(
  procedure
  var
    LResult: Integer;
  begin
    Sleep(1000); //并行做一些事情...
    LResult := Random(100); //获取执行结果 "result"
    //Queue 在主进程中执行(主进程执行方法一)
    TThread.Queue(nil,
      procedure
      begin
        TaskEnd(LResult); //TaskEnd 被主 UI 调用
      end);
     //或者 Synchronize 在主进程中执行(主进程执行方法二)
     TThread.Synchronize(TThread.Current,
       procedure
       begin
         ShowMessage ('Hello');
       end);

   end);
end;

上記の関数は非常に簡単に使用できますが、多数のタスクが実行されており、同時に例外を処理する必要がある場合、物事はそれほど単純ではありません。

この時点で、ITask インターフェイスを拡張できます (例: Async.Run<T> )。Async.Run<T>メソッドの完全な呼び出しは、3 つの匿名メソッドで構成されます。

バックグラウンド タスク: これは、ある種のデータを返す関数です。PPL タスクを使用してバックグラウンド スレッドで実行されます。

成功コールバック:これは、バックグラウンド タスクの結果を取得するために使用されるプロシージャです。メイン UI スレッドで実行されます。

エラー コールバック:これは、バックグラウンド タスクによって発生した例外 (存在する場合) をキャッチするために使用されるプロシージャです。メイン UI スレッドで実行されます。

全体的な構造は次のようになります。

Async.Run<String>(
function: String
begin
//这是 "后台 "匿名方法。运行在后台线程中运行,其结果被传递到
//到 "成功 "回调过程中。
//在当前架构情况下,结果是一个字符串。
end,
procedure(const Value: String)
begin
//这是 "成功 "回调。在UI线程中运行并
//获取 "后台 "匿名方法的结果。
end,
procedure(const Ex: Exception)
begin
//这是 "错误 "回调。
//在UI线程中运行,只有在 "后台 "匿名方法产生异常时才会被调用。
//"后台 "匿名方法引发异常时才会被调用。
end)

完全な AsyncTask.pas ユニットは次のとおりです。

unit AsyncTask;
interface
uses
  System.SysUtils,
  System.Threading; //The PPL unit
type
  //"后台 "任务
  TAsyncBackgroundTask<T> = reference to function: T;
  //"成功 "回调过程
  TAsyncSuccessCallback<T> = reference to procedure(const TaskResult: T);
  //"错误 "回调过程
  TAsyncErrorCallback = reference to procedure(const E: Exception);
  //如果用户没有提供默认的 "错误 "回调,则下面为默认的 "错误 "回调。
  TAsyncDefaultErrorCallback = reference to procedure(const E: Exception;
       const ExptAddress: Pointer);
  //主类
  Async = class sealed   //一个sealed (封闭)的类不能通过继承来扩展。
  public
    class function Run<T>(Task: TAsyncBackgroundTask<T>; 
    Success: TAsyncSuccessCallback<T>;
    Error  : TAsyncErrorCallback = nil): ITask;
  end;
var
  //默认的 "错误 "回调。它是一个公共变量,以便于可以覆盖默认行为
  DefaultTaskErrorHandler: TAsyncDefaultErrorCallback = nil;

implementation
uses
  System.Classes;

class function Async.Run<T>(Task: TAsyncBackgroundTask<T>; 
  Success: TAsyncSuccessCallback<T>;
  Error: TAsyncErrorCallback): ITask;
var
  LRes: T;
begin
  //“后台 ”任务从这里开始
  Result := TTask.Run(
  procedure
  var
    Ex: Pointer;
    ExceptionAddress: Pointer;
  begin
    Ex := nil;
    try
      LRes := Task(); //运行实际的任务
      if Assigned(Success) then
        begin
          //调用成功回调,传递结果。
          TThread.Queue(nil,
          procedure
            begin
              Success(LRes);
            end);
        end;
    except
       //让我们扩充异常对象。
      Ex := AcquireExceptionObject;
      ExceptionAddress := ExceptAddr;
      //在主线程上排队,调用错误回调。
      TThread.Queue(nil,
      procedure
      var
        LCurrException: Exception;
      begin
        LCurrException := Exception(Ex);
        try
          if Assigned(Error) then
            Error(LCurrException) //调用 "错误 "回调
          else
            DefaultTaskErrorHandler(LCurrException, ExceptionAddress);
        finally
          //释放异常对象。它是必要的,因为上面 "扩充 "了异常对象的自然寿命
          //异常对象的自然寿命超过了 except 块
          FreeAndNil(LCurrException);
        end;
      end);
    end; //except
  end); //task.run
end;

initialization

//这是默认的错误回调。
DefaultTaskErrorHandler :=
  procedure(const E: Exception; const ExceptionAddress: Pointer)
  begin
    ShowException(E, ExceptionAddress);
  end;
end.

上記で Async.Run<T> がどのように実装されるかを理解しました。次に、その使用方法を見てみましょう。メインフォームを開き、

btnシンプルなボタン。

procedure TMainForm.btnSimpleClick(Sender: TObject);
begin
Async.Run<Integer>(
 function: Integer //在后台长时间运行
 begin
   Sleep(2000);
   Result := Random(100);
 end,
 procedure(const Value: Integer) //在用户界面中显示结果
  begin
    //将结果写在一个备忘录中
   Log('RESULT: ' + Value.ToString);
  end);
end;

上記の場合、長い操作と偽の操作が匿名メソッドで実行されます。整数を返す関数として。その関数が終了すると、その戻り値は別の匿名メソッドに渡されます。このメソッドはプロシージャであり、UI スレッドで実行されます。

ユーザーと対話できるようにスレッドを作成します。プログラムを実行してボタンをクリックすると、長時間の操作中に UI がフリーズしないことを確認できます。(実際には Sleep(2000) の呼び出し) 実行中に UI はフリーズしません。

2 番目のボタンを btnWithException として配置すると、バックグラウンド スレッドでスローされる可能性のある例外を処理する方法が示されます。

procedure TMainForm.btnWithExceptionClick(Sender: TObject);
begin
Async.Run<String>(
  function: String
  begin
    raise Exception.Create('This is an error message');
  end,
  procedure(const Value: String)
  begin
    // never called
  end,
  procedure(const Ex: Exception)
  begin
    Log('Exception: ' + sLineBreak + Ex.Message);
  end);
end;

とてもシンプルですね。バックグラウンドで何か問題が発生した場合、関連する例外オブジェクトがエラー コールバックに渡されます。このエラーブロックに注意してください

これは標準の Delphi 例外ブロックではなく、実際に例外オブジェクトを取得する単なる匿名メソッドです。

おすすめ

転載: blog.csdn.net/sensor_WU/article/details/129417572
おすすめ