【问题标题】:Handling of local variable in anonymous procedure passed to TThread.Queue处理传递给 TThread.Queue 的匿名过程中的局部变量
【发布时间】:2017-07-07 03:43:03
【问题描述】:

我有一个程序,它使用执行某些工作的线程。该线程应通知另一个线程(在本例中为主线程)进度。

如果我使用 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:我知道频繁更新 UI 没有多大意义。我只想知道是什么让变量内容发生变化,尽管匿名方法应该捕获值。
  • 您确实捕获了变量。但是您正试图捕捉“价值”。所以你需要为循环的每次迭代创建一个新变量,并捕获它。这需要一个新的堆栈框架,因此需要一个函数调用。这导致了 LURD 答案中的代码。

标签: multithreading pascal delphi


【解决方案1】:

匿名过程捕获变量引用。 这意味着匿名过程运行时该值是不确定的。

为了捕获一个值,您必须将它包装到一个独特的框架中,如下所示:

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

【讨论】:

猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2012-12-07
  • 1970-01-01
  • 2011-09-10
  • 2011-11-20
  • 1970-01-01
  • 1970-01-01
  • 2018-09-02
相关资源
最近更新 更多