Aquire Singleton class Instance Multithread

2019-02-26 00:22发布

To get the instance of the class with Singleton pattern, I want use the following function:

This is a sketch

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.

Three Questions:

1- Is this design Thread Safe? Especially the with InterlockedExchange Part.
2- How to use the InterlockedCompareExchange? Is it possible to do what i'm trying?
3- Is this design better than involve all code within the critical section scope?

Remark: My object is thread safe, only the construction i need to serialize!
This is not the intire code, only the important part, i mean, the getInstance function.

Edit

I NEED to use some kind of singleton object.
Is there any way to use InterlockedCompareExchange to compare if the value of InstanceNumber is zero?
And
1 - Create the object only when is 0, otherwise, return the instance.
2 - When the value is 0: Enter in the critical section. Create the object. Leave critical section.
3 - Would be better to do this way, instead of involve all code within the critical section scope?

2条回答
Fickle 薄情
2楼-- · 2019-02-26 00:59

Your design does not work and this can be seen even without any understanding of what InterlockedCompareExchange does. In fact, irrespective of the meaning of InterlockedCompareExchange, your code is broken.

To see this, consider two threads arriving at the if statement in getInstance at the same time. Let's consider the three options for which branches they take:

  1. They both choose the second branch. Then you create two instances and your code no longer implements a singleton.
  2. They both choose the first branch. Then you never create an instance.
  3. One choose the first and the other chooses the second. But since there is no lock in the first branch, the thread that takes that route can read AObject before the other thread has written it.

Personally I'd use double-checked locking if I had to implement your getInstance function.

查看更多
闹够了就滚
3楼-- · 2019-02-26 01:16

There is a technique called "Lock-free initialization" that does what you want:

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;

The use of InterlockedCompareExchangePointer erects a full memory barrier around the operation. It is possible one might be able to get away with InterlockedCompareExchangeRelease to use release semantics (to ensure the construction of the object completes before performing the compare exchange). The problem with that is:

  • i'm not smart enough to know if Release semantics alone really will work (just cause a smarter person than me said they would, doesn't mean i know what he's talking about)
  • you're constructing an object, the memory barrier performance hit is the least of your worries (it's the thread safety)

Note: Any code released into public domain. No attribution required.

查看更多
登录 后发表回答