iTask - 如何使用变量作为TTask过程的参数

JRG*_*JRG 2 delphi multithreading task

我需要创建一些iTasks,它们将在不同的位置填充相同的数组.由于要为每个Task执行的代码是相同的,我决定创建一个iTasks数组并创建4个任务.将参数传递给iTask内的主要程序时出现问题.当我使用变量作为参数时,只考虑创建的最后一个任务的值.当我将参数作为值传递(硬编码)时,它会尊重每个任务的所有值.请看我的代码:

uses
  Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, 
  System.Classes, Vcl.Graphics,
  Vcl.Controls, Vcl.Forms, Vcl.Dialogs,
  UNTThreads, Vcl.StdCtrls,
  System.Threading ;

type
 Vet         = array of integer;

type
  TFMThreadArray = class(TForm)
    EDTArraySize: TEdit;
    EDTNumberofThreads: TEdit;
    Memo1: TMemo;
    LBArraySize: TLabel;
    LBThreads: TLabel;
    BTUsingForLoop: TButton;
    EDTThread: TEdit;
    BTHardCoded: TButton;

    procedure BTUsingForLoopClick(Sender: TObject);
    procedure BTHardCodedClick(Sender: TObject);

  private
    { Private declarations }


    procedure  ProcA ( Const  pin, pfin, Psize, Ptask  : integer;
                       Var   Parray : vet);

  public
    { Public declarations }
  end;

var
  FMThreadArray: TFMThreadArray;

implementation

{$R *.dfm}


// Procedure to be called by each iTask    
    procedure TFMThreadArray.ProcA ( Const  pin, pfin, Psize, Ptask  : integer;
                                      Var   Parray : vet);
    var
        vind : integer;

    begin
          for vind := pin to pfin do
          begin
               Parray[vind]   := vind * 10;
          end;
    end;
Run Code Online (Sandbox Code Playgroud)

==>以下方法BTHardCodedClick产生预期结果.它相应地填充数组.但它在创建4个iTasks和在ProcA中传递参数时是硬编码的.我不想这样实现!

procedure TFMThreadArray.BTHardCodedClick(Sender: TObject);

var
    varray                   : vet;
    ind, indtask             : Integer;
    Ptasks                   : array of iTask;

begin
     memo1.Clear;

     SetLength(PTasks,Strtoint(EDTNumberofThreads.text));
     SetLength(varray,StrToint(EDTarraysize.text));

     // fill array with a initial value -2
     for ind := Low(varray) to High(varray) do
            varray[ind] :=-2;


     // when call ProcA passing values parameters it works propperly
     PTasks[0] := TTask.Create( procedure
                                      begin
                                             ProcA(0,3,16,0,varray) ;
                                      end
                                     ) ;

     PTasks[1] := TTask.Create( procedure
                                      begin
                                             ProcA(4,7,16,1,varray) ;
                                      end
                                     ) ;

     PTasks[2] := TTask.Create( procedure
                                      begin
                                             ProcA(8,11,16,2,varray) ;
                                      end
                                     ) ;

     PTasks[3] := TTask.Create( procedure
                                      begin
                                             ProcA(12,15,16,3,varray) ;
                                      end
                                     ) ;


     for Indtask := Low(Ptasks) to High(Ptasks) do
         Ptasks[Indtask].Start;

     TTask.WaitForAll(Ptasks);

     memo1.Clear;
     memo1.Lines.Add(' ============== Creating TASKs with hard-coded parameters ===============');
     memo1.lines.add(' Array size : '        + EDTArraySize.text +
                      ' number of Tasks : ' + EDTNumberofThreads.text);
     memo1.Lines.Add(' =========================================================');

     for ind := Low(varray) to High(varray) do
          memo1.Lines.Add(' Array position  : '    + Format('%.3d',[ind])  +
                          '   content  : ' + varray[ind].ToString   );

end;
Run Code Online (Sandbox Code Playgroud)

在此输入图像描述

===>以下方法是我想实现的方法但是它不起作用!因为它没有填充数组.似乎只有最后一个iTask"PTasks [indtask]"正在执行.

procedure TFMThreadArray.BTUsingForLoopClick(Sender: TObject);
var
    varray                           : vet;
    Ptasks                           : array of iTask;
    vind, indtask, vslice            : Integer;
    vfirst, vlast, vthreads, vsize   : Integer;

begin

     vthreads := Strtoint(EDTNumberofThreads.text);
     vsize    := StrToint(EDTArraysize.text);

     SetLength(PTasks,vthreads);
     SetLength(varray,vsize);

     for vind := Low(varray) to High(varray) do
            varray[vind]:=-33;

     vslice := Length(varray) div vthreads;


     for indtask := Low(PTasks) to High(PTasks) do
     begin
          vfirst := indtask       * vslice;
          vlast  := (indtask + 1) * vslice - 1;

          if (Length(varray) mod vthreads <> 0) and (indtask = High(Ptasks)) then
               vlast := HIgh(varray);

          PTasks[indtask] := TTask.Create( procedure
                                            begin
                                                  procA(vfirst,vlast,vsize,indtask,varray) ;
                                            end
                                           ) ;
     end;

     // Starting all Tasks
     for Indtask := Low(Ptasks) to High(Ptasks) do
         Ptasks[Indtask].Start;

     // Waits until all Tasks been concluded
     TTask.WaitForAll(Ptasks);

     memo1.Clear;
     memo1.Lines.Add(' =============  Using For Loop to create the TASKs =====================');
     memo1.lines.add(' Array size : '        + EDTArraySize.text +
                      ' number of Tasks : ' + EDTNumberofThreads.text);
     memo1.Lines.Add(' =========================================================');


     for vind := Low(varray) to High(varray) do
         memo1.Lines.Add(' Array position  : '    + Format('%.3d',[vind])  +
                         '   content  : '         + varray[vind].ToString   );

end;

end.
Run Code Online (Sandbox Code Playgroud)

在此输入图像描述 我无法理解为什么在iTask中调用procA(vfirst,vlast,vsize,indtask,varray)不考虑参数vfirst,vlast的值.在此先感谢您的帮助 !

Dal*_*kar 5

您观察到的效果是由于匿名方法变量捕获机制.它不会在代码执行期间捕获特定点的变量值,而是捕获变量的位置.

由于所有任务都在创建它们的循环之后运行,因此您将只看到存储的最后一个值.

要解决您的问题,您必须添加其他功能,以确保您不会捕获任务中的常见变量.

function CreateTask(vfirst, vlast, vsize, indtask: integer; var varray: Vet): ITask;
var
  va: Vet;
begin
  // var parameter cannot be captured so we have to store it into
  // local variable - dynamic arrays act like pointers and any changes
  // to local variable will actually change the original too        
  va := varray;
  Result := TTask.Create(
        procedure
        begin
          ProcA(vfirst, vlast, vsize, indtask, va);
        end);
end;
Run Code Online (Sandbox Code Playgroud)

然后你称之为

  Ptasks[indtask] := CreateTask(vfirst, vlast, vsize, indtask, varray);
Run Code Online (Sandbox Code Playgroud)

当然,如果符合您的需要,您也可以删除您的ProcA程序并将其逻辑直接合并到CreateTask函数中.