我需要创建多个iTask,这些iTask会将相同的数组填充到不同的位置。由于每个任务要执行的代码相同,因此我决定创建一个iTasks数组并创建4个任务。将参数传递给iTask内部的主要过程时遇到问题。当我使用变量作为参数时,仅考虑最后创建的Task的值。当我将参数作为值(硬编码)传递时,它将尊重每个任务的所有值。请查看我的代码:
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;
==>此下面的方法BTHardCodedClick产生预期的结果。它相应地填充数组。但是,在创建4个iTask和在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;
===>以下方法是我要实现的一种方法,但是它没有工作!,因为它没有填充数组。似乎仅执行了最后一个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.
我不明白为什么在iTask中对procA(vfirst,vlast,vlast,vsize,indtask,varray)的调用没有考虑参数vfirst,vlast的值。 预先感谢您的帮助!
答案 0 :(得分: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;
然后您称呼它
Ptasks[indtask] := CreateTask(vfirst, vlast, vsize, indtask, varray);
当然,如果需要,您也可以删除ProcA
过程并将其逻辑直接合并到CreateTask
函数中。