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