要使用Singleton模式获取类的实例,我想使用以下函数:
这是草图
interface
uses SyncObjs;
type
TMCriticalSection = class(TCriticalSection)
private
Dummy : array [0..95] of Byte;
end;
var
InstanceNumber : Integer;
AObject: TObject;
CriticalSection: TMCriticalSection;
function getInstance: TObject;
implementation
uses Windows;
function getInstance: TObject;
begin
//I Want somehow use InterlockedCompareExchange instead of CriticalSession, for example
if InterlockedCompareExchange(InstanceNumber, 1, 0) > 0 then
begin
Result := AObject;
end
else
begin
CriticalSection.Enter;
try
AObject := TObject.Create;
finally
CriticalSection.Leave;
end;
InterlockedIncrement(InstanceNumber);
Result := AObject
end;
end;
initialization
CriticalSection := TMCriticalSection.Create;
InstanceNumber := 0;
finalization;
CriticalSection.Free;
end.
三个问题:
1-此设计线程安全吗?尤其是InterlockedExchange Part。
2-如何使用InterlockedCompareExchange?有可能做我正在尝试的事情吗?
3-这种设计是否比涉及临界区范围内的所有代码更好?
注:
我的对象是线程安全的,只有我需要序列化的构造!
这不是服装代码,只是重要部分,我的意思是getInstance函数。
我需要使用某种单例对象。
有没有办法使用InterlockedCompareExchange来比较InstanceNumber的值是否为零?
和
1 - 仅在为0时创建对象,否则返回实例。
2 - 值为0时:输入临界区。创建对象。离开关键部分。
3 - 以这种方式做得更好,而不是将所有代码都包含在关键部分范围内?
答案 0 :(得分:4)
您的设计不起作用,即使不了解InterlockedCompareExchange
的作用,也可以看到这一点。事实上,无论InterlockedCompareExchange
的含义如何,您的代码都会被破坏。
要看到这一点,请考虑两个线程同时到达if
中的getInstance
语句。让我们考虑他们采取分支的三个选项:
AObject
。如果我必须实施getInstance
功能,我个人会使用double-checked locking。
答案 1 :(得分:3)
有一种名为"Lock-free initialization"的技术能够满足您的需求:
interface
function getInstance: TObject;
implementation
var
AObject: TObject;
function getInstance: TObject;
var
newObject: TObject;
begin
if (AObject = nil) then
begin
//The object doesn't exist yet. Create one.
newObject := TObject.Create;
//It's possible another thread also created one.
//Only one of us will be able to set the AObject singleton variable
if InterlockedCompareExchangePointer(AObject, newObject, nil) <> nil then
begin
//The other beat us. Destroy our newly created object and use theirs.
newObject.Free;
end;
end;
Result := AObject;
end;
InterlockedCompareExchangePointer
的使用在操作周围建立了完整的记忆障碍。有可能可以使用InterlockedCompareExchangeRelease
来使用发布语义(以确保在执行比较交换之前完成对象的构造)。问题是:
注意:任何已发布到公共领域的代码。无需归属。