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

时间:2015-03-08 23:31:49

标签: multithreading delphi delphi-xe6

鉴于以下情况,匿名线程读取的DoSomething中AValue的值是否保证“可读”?即,我期待的价值?对我来说,我认为并不是因为DoSomething在线程实际执行之前超出了范围(即返回)(通过认识到x:= 2行总是在我的线程开始之前执行而很容易看出 - 虽然我想用线程所有投注都关闭,我的线程可能在DoSomething返回之前执行。)

我只是问,因为我在测试中从未遇到过AValue不等于1的情况(也就是传入的值)所以我想知道是否对程序和/或线程持有一些隐式引用(再次不太可能,因为CreateAnonymousMethod只是创建一个TThread后代的实例(TAnonymousThread)并调用我的匿名“执行”方法)。我猜测它是相同的,因为没有(在这种有限的情况下)覆盖了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;

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

1 个答案:

答案 0 :(得分:2)

您正在按值传递x。这意味着当您调用DoSomething()时会复制x的值。

因此,每当执行匿名线程时,它都没有引用x。 该线程使用了捕获的变量,该变量使用原始值x进行初始化。

换句话说,匿名线程无法看到您是否在ButtonClick1事件中更改了x。


注意,如果在构造匿名线程后更改AValue中的本地DoSomething(),则会影响线程的结果。

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是通过匿名方法引用捕获的:

  

如果匿名方法引用其正文中的外部局部变量,则该变量将被捕获&#34;。捕获意味着延长变量的生命周期,使其与匿名方法值一样长,而不是死于其声明例程。请注意,变量捕获捕获变量 - 而不是值。如果变量的值在通过构造匿名方法捕获后发生更改,则捕获的匿名方法的变量值也会更改,因为它们是具有相同存储的相同变量。捕获的变量存储在堆上,而不是堆栈中。