Embarcadero 的文档对此进行了详细介绍:
Anonymous Methods in Delphi: Variable Binding Mechanism
如果匿名方法在其主体中引用外部局部变量,则该变量被“捕获”。捕获意味着延长变量的生命周期,使其与匿名方法值一样长,而不是随着它的声明例程而死。 请注意,变量捕获捕获的是变量——而不是值。如果构造匿名方法捕获后变量的值发生变化,那么匿名方法捕获的变量的值也会发生变化,因为它们是同一个变量,具有相同的存储空间。捕获的变量存储在堆上,而不是存储在堆上。堆栈。
然后文档更详细地解释了实现如何捕获变量,尤其是当多个匿名方法捕获相同的变量时(如您的示例中的情况)。
这种情况在多个匿名方法捕获同一个局部变量的情况下更加复杂。要了解它在所有情况下的工作原理,有必要更准确地了解实现的机制。
(详情如下...)
因此,您的问题与 TTask/PPL 本身无关,而与您正在创建多个共享 j 变量的匿名过程有关。他们正在捕获对变量本身的引用,而不是它的值。当所有任务完成休眠时,j 已设置为其最终值,然后所有任务都会输出该值。
解决方法(在上面的同一文档中也有描述)是让您的循环将变量作为参数传递给中间函数,然后声明匿名方法来捕获参数。这样,每个匿名方法都会捕获不同的变量,例如:
procedure LaunchTask(index: Integer);
var
ltask: ITask;
begin
ltask := TTask.Create(
procedure ()
begin
Sleep(3000);
SendDebugFmt('Task #%d' , [index]);
end);
ltask.Start;
end;
procedure TForm2.LaunchTasks;
const
cmax = 5;
Var
i: Integer;
begin
for i := 1 to cmax do
begin
LaunchTask(i);
end;
end;
另一种解决方案是将循环替换为TParallel.For(),而不是直接使用TTask,例如:
procedure TForm2.LaunchTasks;
const
cmax = 5;
begin
TParallel.&For(1, cmax,
procedure (index: integer)
begin
Sleep(3000);
SendDebugFmt('Task #%d' , [index]);
end);
end;