سؤال

In C#, the following code (from this page) can be used to lazily instantiate a singleton class in a thread safe way:

  class Foo {
        private volatile Helper helper = null;
        public Helper getHelper() {
            if (helper == null) {
                lock(this) {
                    if (helper == null)
                        helper = new Helper();
                }
            }
            return helper;
        }
    }

What would be the equivalent thread safe Delphi code?


The article also mentions two problems with Double Checked Locking in Java:

  • it is possible that the new object is constructed before the helper reference is made to point at the newly created object meaning that two objects are created
  • it is possible that the helper reference is made to point at a block of memory while the object is still being created meaning that a reference to an incomplete object will be returned

So while the code of the C# and the Java version in the mentioned article look almost identical, only the C# version works as expected. Which leads to the additional question if these two problems also exist in a Delphi version of Double-Checked Locking?

هل كانت مفيدة؟

المحلول

Use System.TMonitor to lock the object instance in a thread safe way.

function TFoo.GetHelper(): THelper;
begin
  if not Assigned(FHelper) then
  begin
    System.MonitorEnter(Self);
    try
      if not Assigned(FHelper) then
        FHelper := THelper.Create();
    finally
      System.MonitorExit(Self);
    end;
  end;
  Result := FHelper;
end;

For further reference look at Lock my object..., please! from Allen Bauer. In fact, the rep. I gather from this should go to Allen.

نصائح أخرى

Of course, it's always worth remembering that Double-Checked Locking is Broken. This issue turns out not to apply to the x86 memory model but it's always worth bearing in mind for the future. I'm sure there will be Delphi version at some point that will run on a platform with a memory model that is afflicted by this issue.

Embarcadero have started using a lock-free version of this pattern with interlocked compare/exchange. For example:

class function TEncoding.GetUnicode: TEncoding;
var
  LEncoding: TEncoding;
begin
  if FUnicodeEncoding = nil then
  begin
    LEncoding := TUnicodeEncoding.Create;
    if InterlockedCompareExchangePointer(Pointer(FUnicodeEncoding), LEncoding, nil) <> nil then
      LEncoding.Free;
  end;
  Result := FUnicodeEncoding;
end;

I realise this isn't an answer to the question but it didn't really fit in a comment!

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top