处理传递给 TThread.Queue 的匿名过程中的局部变量
Handling of local variable in anonymous procedure passed to TThread.Queue
我有一个程序使用执行某些工作的线程。该线程应将进度通知另一个线程(在本例中为主线程)。
如果我使用 Synchronize() 来执行同步,一切都会按预期进行。如果我与主线程同步并发布 for 变量并将其放入列表中,每个值都会正确打印到我的列表框中:
procedure TWorkerThread.Execute;
var
i: Integer;
begin
inherited;
for i := 1 to 1000 do
begin
Synchronize(
procedure()
begin
FireEvent(i);
end);
end;
end;
Output: 1, 2, 3, 4, 5 ... 1000
如果我使用 Queue() 执行同步,则输出与预期不同:
procedure TWorkerThread.Execute;
var
i: Integer;
begin
inherited;
for i := 1 to 1000 do
begin
Queue(
procedure()
begin
FireEvent(i);
end);
end;
end;
Output: 200, 339, 562, 934, 1001, 1001, 1001, 1001, 1001, 1001, 1001, 1001, 1001, [...]
这里发生了什么?据我了解,匿名过程应该捕获变量 "i"?
匿名过程捕获变量引用。
这意味着匿名过程运行时该值是不确定的。
为了捕获一个值,您必须将其包装到一个独特的框架中,如下所示:
Type
TWorkerThread = class (TThread)
...
function GetEventProc(ix : Integer): TThreadProcedure;
end;
function TWorkerThread.GetEventProc(ix : Integer) : TThreadProcedure;
// Each time this function is called, a new frame capturing ix
// (and its current value) will be produced.
begin
Result := procedure begin FireEvent(ix); end;
end;
procedure TWorkerThread.Execute;
var
i: Integer;
begin
inherited;
for i := 1 to 1000 do
begin
Queue( GetEventProc(i));
end;
end;
另见 Anonymous methods - variable capture versus value capture。
我有一个程序使用执行某些工作的线程。该线程应将进度通知另一个线程(在本例中为主线程)。
如果我使用 Synchronize() 来执行同步,一切都会按预期进行。如果我与主线程同步并发布 for 变量并将其放入列表中,每个值都会正确打印到我的列表框中:
procedure TWorkerThread.Execute;
var
i: Integer;
begin
inherited;
for i := 1 to 1000 do
begin
Synchronize(
procedure()
begin
FireEvent(i);
end);
end;
end;
Output: 1, 2, 3, 4, 5 ... 1000
如果我使用 Queue() 执行同步,则输出与预期不同:
procedure TWorkerThread.Execute;
var
i: Integer;
begin
inherited;
for i := 1 to 1000 do
begin
Queue(
procedure()
begin
FireEvent(i);
end);
end;
end;
Output: 200, 339, 562, 934, 1001, 1001, 1001, 1001, 1001, 1001, 1001, 1001, 1001, [...]
这里发生了什么?据我了解,匿名过程应该捕获变量 "i"?
匿名过程捕获变量引用。 这意味着匿名过程运行时该值是不确定的。
为了捕获一个值,您必须将其包装到一个独特的框架中,如下所示:
Type
TWorkerThread = class (TThread)
...
function GetEventProc(ix : Integer): TThreadProcedure;
end;
function TWorkerThread.GetEventProc(ix : Integer) : TThreadProcedure;
// Each time this function is called, a new frame capturing ix
// (and its current value) will be produced.
begin
Result := procedure begin FireEvent(ix); end;
end;
procedure TWorkerThread.Execute;
var
i: Integer;
begin
inherited;
for i := 1 to 1000 do
begin
Queue( GetEventProc(i));
end;
end;
另见 Anonymous methods - variable capture versus value capture。