传递给TThread.Queue的匿名过程中局部变量的处理方式

5
我有一个程序使用了一个线程来执行一些工作。该线程应通知另一个线程(在本例中为主线程)进度情况。
如果我使用Synchronize()进行同步,一切都按预期运行。如果我与主线程同步并发布for变量并将其放入列表中,则每个值都会正确打印到我的ListBox中:
procedure TWorkerThread.Execute;
var
  i: Integer;
begin
  inherited;

  for i := 1 to 1000 do
  begin
    Synchronize(
      procedure()
      begin
        FireEvent(i);
      end);
  end;
end;

输出: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;

输出结果: 200、339、562、934、1001、1001、1001、1001、1001、1001、1001、1001、1001、[...]

这里发生了什么?按我的理解,匿名程序应该捕获变量“i”吗?


PS:我知道这么频繁地更新用户界面没有太大意义。我只是想知道什么会导致变量内容改变,即使匿名方法应该捕获该值。 - Ranga B.
1
您IP地址为143.198.54.68,由于运营成本限制,当前对于免费用户的使用频率限制为每个IP每72小时10次对话,如需解除限制,请点击左下角设置图标按钮(手机用户先点击左上角菜单按钮)。 - David Heffernan
1个回答

5
匿名过程捕获变量引用。 这意味着当匿名过程运行时,该值是不确定的。
为了捕获一个值,您需要将其包装到一个唯一的框架中,如下所示:
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;

另请参阅匿名方法 - 变量捕获与值捕获


应该是 Queue(EventWithValue(i)),而且 EventWithValue 应该返回一个 TThreadProcedure。至少,这是我会做的。 - Ken Bourassa
请阅读文档:Delphi中的匿名方法,特别是匿名方法变量绑定 - Remy Lebeau

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接