在匿名线程的上下文中访问局部变量是否安全

Is it safe to access a local variable within the context of an anonymous thread

鉴于以下情况,匿名线程读取的 DoSomething 中的 AValue 值是否保证为 "readable"?即,我期望的价值?对我来说,我不认为在线程实际执行之前 DoSomething 超出范围(即 returns)(通过意识到 x := 2 行总是在我的线程启动之前执行可以很容易地看出这一点 - 虽然我猜线程所有的赌注都关闭了,我的线程 可能 在 DoSomething returns).

之前执行

我之所以问,是因为我在测试中从未遇到过 AValue 不等于 1(也就是传入的值)的情况,所以我想知道是否对过程进行了一些隐式引用 and/or 线程(同样不太可能,因为 CreateAnonymousMethod 只是创建一个 TThread 后代 (TAnonymousThread) 的实例并调用我的匿名 "execute" 方法)。我猜这是一样的,因为没有任何东西(在这种有限的情况下)覆盖存储 AValue 的内存位置。

procedure TForm2.Button1Click(Sender: TObject);
var
  x: Integer;
begin
  x := 1;
  DoSomething(x);
  x := 2;// this line is only here for the purposes of placing a break point
end;

procedure TForm2.DoSomething(AValue: Integer);
begin

  TThread.CreateAnonymousThread(
    procedure
    var
      y: Integer;
    begin
      y := AValue;

      if y = 1 then
        MessageBox(0, 'Same', 'Value', MB_ICONINFORMATION or MB_OK)
      else
        MessageBox(0, 'Different', 'Value', MB_ICONINFORMATION or MB_OK)
    end).Start;
end;

编辑 只是为了验证,我想知道在匿名线程的上下文中捕获局部变量是否安全。

您正在按值传递 x。这意味着当您调用 DoSomething().

时 x 的值被复制

因此,无论何时执行匿名线程,它都没有对 x 的引用。 该线程使用了一个捕获的变量,该变量用 x 的原始值初始化。

换句话说,如果您稍后在 ButtonClick1 事件中更改 x,匿名线程无法看到。


注意,如果在构造匿名线程后更改DoSomething()中的局部AValue,这将影响您线程的结果。

procedure TForm2.DoSomething(AValue: Integer);
begin

  TThread.CreateAnonymousThread(
    procedure
    var
      y: Integer;
    begin
      y := AValue;

      if y = 1 then
        MessageBox(0, 'Same', 'Value', MB_ICONINFORMATION or MB_OK)
      else
        MessageBox(0, 'Different', 'Value', MB_ICONINFORMATION or MB_OK)
    end).Start;
  AValue := 3;  // <-- This value will likely be seen by the anonymous thread.
end;

如果您想避免这种情况,您可以像这样捕获 AValue 值:

procedure TForm4.DoSomething(AValue: Integer);
   function CaptureValue( y: Integer) : TProc;
   begin
     Result :=
      procedure
      begin
       if y = 1 then
         MessageBox(0, 'Same', 'Value', MB_ICONINFORMATION or MB_OK)
       else
         MessageBox(0, 'Different', 'Value', MB_ICONINFORMATION or MB_OK)
     end;
   end;
var
  p : TProc;
begin
  p := CaptureValue(AValue);
  TThread.CreateAnonymousThread( p)
  .Start;
  AValue := 3; // <-- The anonymous method is unaffected by this change !
end;

documentation说明外层局部变量AValue是通过匿名方法引用捕获的:

If an anonymous method refers to an outer local variable in its body, that variable is "captured". Capturing means extending the lifetime of the variable, so that it lives as long as the anonymous method value, rather than dying with its declaring routine. Note that variable capture captures variables--not values. If a variable's value changes after being captured by constructing an anonymous method, the value of the variable the anonymous method captured changes too, because they are the same variable with the same storage. Captured variables are stored on the heap, not the stack.