代码之家  ›  专栏  ›  技术社区  ›  zeus

TThread。具有延迟的ForceQueue

  •  0
  • zeus  · 技术社区  · 3 年前

    当你在 TFrame 而你做到了 TThread.ForceQueue(nil, MyFrame.OneProc, 200) 你怎么办理登机手续 MyFrame.OneProc 程序 MyFrame 同时没有被摧毁?

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

    1 回复  |  直到 3 年前
        1
  •  4
  •   Dalija Prasnikar    3 年前

    您可以使用 守护者 接口,它将是一个功能齐全的实例,您可以使用它来检查受保护的对象是否同时被释放。

    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 在没有延迟的情况下,有可能在排队过程运行之前释放帧。所以在这种情况下,你也需要保护你的身体。

        2
  •  4
  •   Remy Lebeau    3 年前

    不能对已销毁的对象调用方法。这个 首选 解决方案是简单地从队列中删除方法(如果尚未调用该方法), 之前 破坏物体。 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;