Потеряна/изменена переменная потока после передачи объекта потока другому классу?

56
6

Я написал небольшую программу, которая показывает мою проблему:

unit Unit1;

interface

uses
Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls;

type
TForm1 = class(TForm)
Button1: TButton;
procedure Button1Click(Sender: TObject);
private
{ Private-Deklarationen }
public
{ Public-Deklarationen }
end;
TThread_A = class(TThread)
private
stupidvariable : integer;
protected
procedure Execute; override;
public
property getstupidvar : integer read stupidvariable;
constructor Create;
end;

TSomeClass = class
private
m_Obj : ^TThread_A;
procedure readVar;
public
constructor Create(obj: TThread_A);
end;

var
Form1: TForm1;

implementation

{$R *.dfm}

constructor TSomeClass.Create(obj: TThread_A);
begin
m_Obj := @obj;
readVar;
end;

procedure TSomeClass.readVar;
begin
showmessage(inttostr(m_Obj.getstupidvar));
end;

constructor TThread_A.Create;
begin
inherited Create(false);
FreeOnTerminate := True;
end;

procedure TThread_A.Execute;
begin
stupidvariable := 100;
end;

procedure TForm1.Button1Click(Sender: TObject);
var
threadA : TThread_A;
someClass : TSomeClass;
begin
threadA := TThread_A.Create;
someClass := TSomeClass.Create(threadA);
end;

end.

Что здесь происходит? Я думал, что передаю объект "threadA" в someClass и присваиваю адрес "threadA" "m_Obj".

Почему объект потерян?

спросил(а) 2017-01-12T15:19:00+03:00 2 года, 10 месяцев назад
1
Решение
56

constructor TSomeClass.Create(obj: TThread_A);
begin
m_Obj := @obj;
readVar;
end;

Здесь obj (по существу) локальная переменная, и поэтому ее время жизни заканчивается, когда функция возвращается. Поэтому вы вспомнили адрес того, что больше не существует.

На самом деле у вас слишком много ссылок. Поскольку TThread_A является классом, а классы являются ссылочными типами, он уже является указателем. + Изменить

m_Obj: ^TThread_A;

в

m_Obj: TThread_A;

а также

m_Obj := @obj;

в

m_Obj := obj;

Теперь вы берете копию ссылки на экземпляр, что я считаю, что вы хотите сделать.

Однако даже это не оставит вас с рабочей программой. Поскольку вы устанавливаете FreeOnTerminate поток может быть уничтожен в любое время. Это означает, что вы не должны ссылаться на него, поскольку эта ссылка может стать недействительной за вашей спиной. Таким образом, вы также должны установить FreeOnTerminate в False.

Однако даже это не оставит вас с предикативным результатом. Процедура потока выполняется независимо от основного потока. Когда вы читаете переменную из основного потока, поток может или не может модифицировать переменную. Это известно как гонка данных. Если вы хотите подождать до тех пор, пока переменная не будет изменена, вы можете использовать, например, объект события, чтобы поток мог сигнализировать, что переменная готова к чтению.

ответил(а) 2017-01-12T15:27:00+03:00 2 года, 10 месяцев назад
Ваш ответ
Введите минимум 50 символов
Чтобы , пожалуйста,
Выберите тему жалобы:

Другая проблема