首页 > 解决方案 > 延迟检测用 TThread.ForceQueue() 调用的已释放对象

问题描述

当您在a中时TFrame,您TThread.ForceQueue(nil, MyFrame.OneProc, 200)如何检查同时没有被破坏的MyFrame.OneProc程序MyFrame

换句话说,在这种常见的场景中可以使用什么机制?

标签: delphi

解决方案


您可以使用监护人接口,该接口将成为功能齐全的实例,您可以使用它来检查受保护对象是否同时被释放。

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没有延迟使用,也有可能在排队过程运行之前释放帧。所以你需要保护你的框架也是这样的场景。


推荐阅读