Если я запущу этот код, результат будет неожиданным, поскольку строки в потоке не имеют правильных значений:
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;
Следует ли этого ожидать из-за того, что строки особенные?
Это не имеет ничего общего со строками, а с тем, как анонимные процедуры захватывают переменные. Это документированное поведение:
Анонимные методы в Delphi > Привязка переменных анонимных методов
В частности, в разделе «Механизм связывания переменных»:
Если анонимный метод ссылается на внешнюю локальную переменную в своем теле, эта переменная «захватывается». Захват означает продление времени жизни переменной, чтобы она жила до тех пор, пока сохраняется значение анонимного метода, а не умирала вместе с подпрограммой объявления. Обратите внимание, что захват переменных фиксирует переменные, а не значения. Если значение переменной изменяется после ее захвата путем создания анонимного метода, значение переменной, захваченной анонимным методом, также изменяется, поскольку это одна и та же переменная с одним и тем же хранилищем. Захваченные переменные хранятся в куче, а не в стеке.
Вы используете одни и те же локальные переменные 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()
.