在Delphi中使用TMonitor阻止对全局对象的访问

问题描述 投票:0回答:1

我在 Delphi 中有一个全局对象,它从磁盘读取一些对象定义并创建代表它们的工作对象的缓存。
全局对象上还有一个返回工作对象实例的方法 - 如果全局对象仍在读取磁盘/创建工作人员的方法内,则需要阻止此方法。此加载/创建方法将间歇性地调用(每天一次)以强制重新加载 xml 文件。

我创建了代码的简化版本,以便我可以获得一些有关正确使用 TMonitor 记录的反馈。 首先是声明

type
  // a simplified version of the worker, I just get it to return the timestamp of when it was created - in real life this actually does something :)
  TWorker = class
  private
    FValue: TDateTime;
  public
    property Value: TDatetime read FValue;
  end;

// An interface modelling the global object
  IStorage = interface
    ['{8FD599BE-4064-45DE-8FFC-96A9D2C812F1}']
    function GetWorker: TWorker;
    procedure ReLoad;
  end;

// a global pointer to a function that has access to a singleton instance of the worker. Creating this 
var
  Storage: function: IStorage;

现在实施

// a concrete implementation of the interface
type
  TStorage = class(TInterfacedObject, IStorage)
  private
    FIsLoaded: Boolean;  // flag to indicate if objects have been loaded
    FList: TObjectList<TWorker>;  // list of Worker objects (takes ownership)
  protected
    { IStorage }
    function GetWorker: TWorker;
    procedure Load;
  public 
    constructor Create;
    destructor Destroy; override;
  end;

// the method to "load/reload" from disk - I've just simplified this-in real life it would lock then clear the cache and recreate the objects
procedure TStorage.ReLoad;
begin
  TMonitor.Enter(Self);
  try
    FIsLoaded := False;
    for var I := 0 to 15 do
      Sleep(250);
    FList.Clear;
    FIsLoaded := true;
    TMonitor.PulseAll(Self);
  finally
    TMonitor.Exit(Self);
  end;
end;

// and the method that serves out instances of worker objects
// this is the one that is causing me to double check my understanding of TMonitor
function TStorage.GetWorker: TWorker;
begin
  if not FIsLoaded then
  begin
    TMonitor.Enter(Self);
    try
      while not FIsLoaded do
        TMonitor.Wait(Self, INFINITE);
    finally
      TMonitor.Exit(Self);
    end;
  end;
  Result := TWorker.Create;
  Result.FValue := Now;
end;

我使用 TMonitor 实例(锁定全局对象)来阻止调用线程继续执行 GetWorker,直到布尔值 FIsLoaded 设置为 true。

我走在正确的道路上吗?特别是我对 Wait 和 PulseAll 方法的使用。

如果这有什么区别的话,我正在使用 Delphi 11.3,因为谷歌搜索表明使用 TMonitor 存在一些问题,但大约 10 年前。

delphi thread-safety tmonitor
1个回答
0
投票

您走在正确的道路上。我建议使用锁的私有实例字段而不是 Self。这将远离“锁定这个(自我)不好”的问题。否则,某些外部“坏人”可能会获取您在内部使用的相同锁定。

© www.soinside.com 2019 - 2024. All rights reserved.