提问人:David Moorhouse 提问时间:11/16/2023 更新时间:11/16/2023 访问量:126
在 Delphi 中使用 TMonitor 阻止对全局对象的访问
Using TMonitor in Delphi to block access to global object
问:
我在 Delphi 中有一个全局对象,它从磁盘中读取一些对象定义,并创建一个表示它们的工作器对象的缓存。
全局对象上还有一个方法,它返回工作器对象的实例 - 如果全局对象仍在读取磁盘/创建工作器的方法中,则需要阻止此方法。此 load/create 方法将间歇性地(每天一次)调用,以强制重新加载 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 年前。
答:
1赞
Allen Bauer
11/16/2023
#1
你走在正确的轨道上。我建议对锁使用私有实例字段而不是 Self。这将远离“锁定这个(自我)是坏的”问题。否则,一些外部“不良行为者”可能会占用您在内部使用的相同锁。
如果你有 System.pas 源代码,应该有一个相当大的注释来描述 TMonitor,包括一个描述其灵感的文章的链接。
评论