【发布时间】:2021-11-07 11:57:16
【问题描述】:
当您在TFrame 中并执行TThread.ForceQueue(nil, MyFrame.OneProc, 200) 时,如何在MyFrame.OneProc 过程中检查MyFrame 同时没有被破坏?
也就是说,在这种常见的场景中可以使用什么机制?
【问题讨论】:
标签: delphi
当您在TFrame 中并执行TThread.ForceQueue(nil, MyFrame.OneProc, 200) 时,如何在MyFrame.OneProc 过程中检查MyFrame 同时没有被破坏?
也就是说,在这种常见的场景中可以使用什么机制?
【问题讨论】:
标签: delphi
您可以使用 guardian 接口,该接口将是功能齐全的实例,您可以使用它来检查受保护的对象是否同时被释放。
type
IGuardian = interface
function GetIsDismantled: Boolean;
procedure Dismantle;
property IsDismantled: Boolean read GetIsDismantled;
end;
TGuardian = class(TInterfacedObject, IGuardian)
private
FIsDismantled: Boolean;
function GetIsDismantled: Boolean;
public
procedure Dismantle;
property IsDismantled: Boolean read GetIsDismantled;
end;
procedure TGuardian.Dismantle;
begin
FIsDismantled := True;
end;
function TGuardian.GetIsDismantled: Boolean;
begin
Result := FIsDismantled;
end;
然后你需要在你的框架中添加监护人字段
type
TMyFrame = class(TFrame)
private
FGuardian: IGuardian;
public
constructor Create(AOwner: TComponent); override;
destructor Destroy; override;
property Guardian: IGuardian read FGuardian;
end;
constructor TMyFrame.Create(AOwner: TComponent);
begin
inherited;
FGuardian := TGuardian.Create;
end;
destructor TMyFrame.Destroy;
begin
// prevent AV when destroying partially
// constructed instance
if Assigned(FGuardian) then
FGuardian.Dismantle;
inherited;
end;
但是您不能直接将帧的MyProc 排队,您需要使用匿名方法并捕获该监护人变量,使其生命周期延长到帧的生命周期之外。
即使在MyFrame 被释放后,引用计数仍将保持守护者对象实例存活,并且其内存将被自动管理。
使用本地声明的Guardian接口变量并捕获该变量而不是直接捕获MyFrame.Guardian字段很重要,因为该字段地址在MyFrame被释放后将不再有效。
procedure CallMyProc;
var
Guardian: IGuardian;
begin
Guardian := MyFrame.Guardian;
TThread.ForceQueue(nil,
procedure
begin
if Guardian.IsDismantled then
Exit;
MyFrame.OneProc;
end, 200);
end;
注意:即使您立即使用TThread.Queue,也有可能在排队过程运行之前释放帧。所以你需要保护你的框架也是这样的场景。
【讨论】:
您不能在已销毁的对象上调用方法。 首选的解决方案是,如果该方法尚未被调用,在销毁对象之前简单地从队列中删除该方法。 TThread 有一个 RemoveQueuedEvents() 方法正是为了这个目的。
例如:
TThread.ForceQueue(nil, MyFrame.OneProc, 200);
...
TThread.RemoveQueuedEvents(MyFrame.OneProc);
MyFrame.Free;
或者,使用框架的析构函数:
TThread.ForceQueue(nil, MyFrame.OneProc, 200);
...
destructor TMyFrame.Destroy;
begin
TThread.RemoveQueuedEvents(OneProc);
inherited;
end;
【讨论】: