2

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

这是草图

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 的部分。
2- 如何使用 InterlockedCompareExchange?有可能做我正在尝试的事情吗?
3- 这种设计是否比包含关键部分范围内的所有代码更好?

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

编辑

需要使用某种单例对象。
如果 InstanceNumber 的值为零,有什么方法可以使用 InterlockedCompareExchange 进行比较?
And
1 - 仅当为 0 时才创建对象,否则返回实例。
2 - 当值为 0 时:进入临界区。创建对象。离开临界区。
3 - 这样做会更好,而不是涉及关键部分范围内的所有代码?

4

2 回答 2

5

有一种称为“无锁初始化”的技术可以满足您的需求:

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使用释放语义(以确保对象的构造在执行比较交换之前完成)是可能 的。问题在于:

  • 我不够聪明,无法知道仅Release语义是否真的会起作用(只是导致比我说的更聪明的人,并不意味着我知道他在说什么)
  • 您正在构建一个对象,内存屏障性能影响是您最不担心的(这是线程安全)

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

于 2014-07-08T16:00:46.240 回答
4

您的设计不起作用,即使不了解做什么,也可以看出这InterlockedCompareExchange一点。实际上,无论 的含义如何InterlockedCompareExchange,您的代码都已损坏。

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

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

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

于 2013-05-17T13:00:38.157 回答