如果我运行此代码,结果会出乎意料,因为线程中的字符串没有正确的值:

procedure TMain.FollowBose(Speakername: string; NewState: Boolean);
{..}
procedure TMain.ProcessRoomMotionResponse(Rooms: TJSONArray; Response: TJSONObject);
var
  ParamName1: string;
  ParamName2: string;
  Room: tJSONValue;
  RoomData: TJSONObject;
  Roomname: string;
  Motion: Boolean;
begin
  if Assigned(Rooms) then begin
    ParamName1 := 'Room';
    ParamName2 := 'Motion';
    for Room in Rooms do begin
      RoomData := Room as TJSONObject;
      if Assigned(RoomData.Values[ParamName1]) and Assigned(RoomData.Values[ParamName2]) then begin
        Roomname := RoomData.Values[ParamName1].Value;
        Motion := RoomData.Values[ParamName2].AsType<Boolean>;
        LogWrite('Launching sub for ' + Roomname + '-FollowUp.', Debug);
        TThread.CreateAnonymousThread(
          procedure
          begin
            FollowUp(Roomname, Motion);
          end).Start;
      end;
    end;
  end;
end;

由于字符串很特殊,这是可以预料到的吗?


1 个回答
1

这与具体的字符串无关,而是与匿名过程如何捕获变量有关。这是有记录的行为:

特别是,根据“变量绑定机制”部分:

如果匿名方法引用其主体中的外部局部变量,则该变量将被“捕获”。捕获意味着延长变量的生命周期,使其与匿名方法值一样长,而不是随着其声明例程而消亡。请注意,变量捕获捕获的是变量,而不是。如果通过构造匿名方法捕获变量的值发生变化,则匿名方法捕获的变量的值也会发生变化,因为它们是相同的变量,具有相同的存储空间。捕获的变量存储在堆上,而不是堆栈上。

您与所有线程共享相同的局部Roomname变量Motion,而不是为每个线程提供自己的变量副本。当循环运行时,您正在更改所有线程共享的变量的值。

试试这个:

procedure TMain.ProcessRoomMotionResponse(Rooms: TJSONArray; Response: TJSONObject);
var
  ParamName1: string;
  ParamName2: string;
  Room: tJSONValue;
  RoomData: TJSONObject;
  Roomname: string;
  Motion: Boolean;

  procedure DoFollowUp(ARoomName: string; AMotion: Boolean);
  begin
    LogWrite('Launching sub for ' + ARoomName + '-FollowUp.', Debug);
    TThread.CreateAnonymousThread(
      procedure
      begin
        FollowUp(ARoomName, AMotion);
      end).Start;
  end;

begin
  if Assigned(Rooms) then begin
    ParamName1 := 'Room';
    ParamName2 := 'Motion';
    for Room in Rooms do begin
      RoomData := Room as TJSONObject;
      if Assigned(RoomData.Values[ParamName1]) and Assigned(RoomData.Values[ParamName2]) then begin
        Roomname := RoomData.Values[ParamName1].Value;
        Motion := RoomData.Values[ParamName2].AsType<Boolean>;
        DoFollowUp(Roomname, Motion);
      end;
    end;
  end;
end;

这样,每个线程都会捕获自己的输入参数副本DoFollowUp(),而不是捕获 的局部变量ProcessRoomMotionResponse()

1

  • 哈哈,这个“DoFollowUp”改变了一切! 😀 谢谢


    –