获取单例类实例多线程



要获取具有单例模式的类的实例,我想使用以下函数:

这是一张草图

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-这种设计线程安全吗?尤其是带有互锁交换部分。
2- 如何使用联锁比较交换?可以做我正在尝试的事情吗?
3-这种设计是否比涉及关键部分范围内的所有代码更好?

备注:我的对象是线程安全的,只有我需要序列化的结构!
这不是inti代码,只是重要的部分,我的意思是getInstance函数。

编辑

我需要使用某种单例对象。
有没有办法使用InterlockedCompareExchange来比较InstanceNumber的值是否为零?

1 - 仅在为 0 时创建对象,否则返回实例。
2 - 当值为 0 时:在关键部分中输入。创建对象。离开关键部分。
3 - 这样做会更好,而不是让所有代码都参与关键部分的范围?

有一种称为"无锁初始化">的技术可以执行您想要的操作:

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(以确保在执行比较交换之前完成对象的构造(。 问题是:

    我不够聪明,
  • 不知道单独的发布语义是否真的有效(只是因为一个比我说的更聪明的人,并不意味着我知道他在说什么(
  • 您正在构造一个对象,内存屏障性能影响是您最不担心的(这是线程安全性(

注意:发布到公共领域的任何代码。无需署名。

你的设计不起作用,即使不了解InterlockedCompareExchange做什么,也可以看到这一点。事实上,不管InterlockedCompareExchange的含义如何,你的代码都被破坏了。

若要查看这一点,请考虑两个线程同时到达 getInstance 中的 if 语句。让我们考虑一下他们采用哪些分支的三个选项:

  1. 他们都选择第二个分支。然后创建两个实例,并且代码不再实现单一实例。
  2. 他们都选择第一个分支。然后,您将永远不会创建实例。
  3. 一个选择第一个,另一个选择第二个。但是由于第一个分支中没有锁,因此采用该路由的线程可以在另一个线程写入之前读取AObject

就个人而言,如果我必须实现您的getInstance功能,我会使用双重检查锁定。

最新更新