5

在 C# 中,以下代码(来自页面)可用于以线程安全的方式延迟实例化单例类:

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

什么是等效的线程安全 Delphi 代码?


文章还提到了 Java 中双重检查锁定的两个问题:

  • 有可能在帮助器引用指向新创建的对象之前构造了新对象,这意味着创建了两个对象
  • 有可能在对象仍在创建时将辅助引用指向内存块,这意味着将返回对不完整对象的引用

因此,虽然上述文章中的 C# 和 Java 版本的代码看起来几乎相同,但只有 C# 版本按预期工作。如果这两个问题也存在于 Delphi 版本的双重检查锁定中,这会导致额外的问题?

4

2 回答 2

7

使用 System.TMonitor 以线程安全的方式锁定对象实例。

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;

如需进一步参考,请查看锁定我的对象...,拜托!来自艾伦鲍尔。事实上,代表。我从这里收集到应该去艾伦。

于 2010-12-17T22:24:59.250 回答
2

当然,始终值得记住Double-Checked Locking is Broken。事实证明,这个问题不适用于 x86 内存模型,但在未来始终值得牢记。我敢肯定,在某个时候,Delphi 版本会在内存模型受此问题困扰的平台上运行。

Embarcadero 已经开始使用这种模式的无锁版本,带有互锁的比较/交换。例如:

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;

我意识到这不是问题的答案,但它并不真正适合评论!

于 2010-12-18T11:06:20.743 回答