Singleton的简单实现

这不是一个更简单,更安全(因此更好)的方式来实现单例而不是双重检查锁定mambo-jambo? 这种方法有什么缺点吗?

public class Singleton { private static Singleton _instance; private Singleton() { Console.WriteLine("Instance created"); } public static Singleton Instance { get { if (_instance == null) { Interlocked.CompareExchange(ref _instance, new Singleton(), null); } return _instance; } } public void DoStuff() { } } 

编辑:线程安全测试失败,谁能解释为什么? 为什么Interlocked.CompareExchange不是真正的primefaces?

 public class Program { static void Main(string[] args) { Parallel.For(0, 1000000, delegate(int i) { Singleton.Instance.DoStuff(); }); } } Result (4 cores, 4 logical processors) Instance created Instance created Instance created Instance created Instance created 

您可能正在创建多个实例,但这些实例将被垃圾收集,因为它们不会在任何地方使用。 在任何情况下,静态_instance字段变量都不会多次更改其值,即从null变为有效值的单次时间。 因此,尽管已经创建了多个实例,但此代码的使用者将只看到相同的实例。

锁定免费编程

Joe Duffy在他的书“ Windows上的并发编程”实际上分析了你试图在第10章“内存模型和锁定自由”中使用的这种模式,第526页。

他将此模式称为轻松引用的Lazy初始化:

 public class LazyInitRelaxedRef where T : class { private volatile T m_value; private Func m_factory; public LazyInitRelaxedRef(Func factory) { m_factory = factory; } public T Value { get { if (m_value == null) Interlocked.CompareExchange(ref m_value, m_factory(), null); return m_value; } } ///  /// An alternative version of the above Value accessor that disposes /// of garbage if it loses the race to publish a new value. (Page 527.) ///  public T ValueWithDisposalOfGarbage { get { if (m_value == null) { T obj = m_factory(); if (Interlocked.CompareExchange(ref m_value, obj, null) != null && obj is IDisposable) ((IDisposable)obj).Dispose(); } return m_value; } } } 

正如我们所看到的,在上面的示例中,方法是免费锁定的,其代价是创建一次性对象。 在任何情况下,Value属性都不会因此类API的使用者而改变。

权衡利弊

锁定自由需要付出代价,并且需要仔细选择您的权衡。 在这种情况下,锁定自由的代价是您必须创建不会使用的对象实例。 这可能是一个可接受的支付价格,因为您知道通过无锁,可以降低死锁风险以及线程争用。

然而,在这个特定的实例中,单例的语义本质上是创建一个对象的单个实例,所以我更愿意选择Lazy因为@Centro在他的回答中引用了。

尽管如此,它仍然存在问题,我们什么时候应该使用Interlocked.CompareExchange ? 我喜欢你的例子,这是非常发人深省的,很多人很快就把它当错,因为@Blindy引用它并不是非常可怕的错误。

这一切都归结为你是否计算了权衡并决定:

  • 你只生产一个实例有多重要?
  • 锁定免费有多重要?

只要你意识到权衡取舍并让它有意识地决定创建新对象以获得无锁,那么你的例子也可以是一个可以接受的答案。

如果您的单身人士有多次自我初始化的危险,那么您的问题会更严重。 为什么不使用:

 public class Singleton { private static Singleton instance=new Singleton(); private Singleton() {} public static Singleton Instance{get{return instance;}} } 

在初始化方面绝对是线程安全的。

编辑:如果我不清楚,你的代码是非常错误的if check和new都不是线程安全的! 你需要使用一个合适的单例类。

为了不使用“双重检查锁定mambo-jambo”或者根本不使用自己的单例重新发明轮子,请使用.NET 4.0中包含的现成解决方案 – Lazy

 public class Singleton { private static Singleton _instance = new Singleton(); private Singleton() {} public static Singleton Instance { get { return _instance; } } } 

我不相信你完全可以相信这一点。 是的,Interlocked.CompareExchanger是primefaces的,但新的Singleton()在任何非平凡的情况下都不会是primefaces的。 由于在交换值之前必须进行评估,因此这通常不是线程安全的实现。

那这个呢?

 public sealed class Singleton { Singleton() { } public static Singleton Instance { get { return Nested.instance; } } class Nested { // Explicit static constructor to tell C# compiler // not to mark type as beforefieldinit static Nested() { } internal static readonly Singleton instance = new Singleton(); } } 

这是此页面上的第五个版本: http : //www.yoda.arachsys.com/csharp/singleton.html

我不确定,但作者似乎认为它的线程安全和延迟加载。

您的单例初始化程序的行为完全符合预期。 请参阅Raymond Chen的无锁算法:单例构造函数 :

这是一个双重检查锁,但没有锁定。 在进行初始构建时,我们只是让它成为一个免费的人来创建对象,而不是锁定。 如果五个线程同时到达此代码,当然,让我们创建五个对象。 在每个人创建他们认为是获胜对象的东西之后,他们调用InterlockedCompareExchangePointerRelease来尝试更新全局指针。

当可以让多个线程尝试创建单例(并让所有输家都销毁他们的副本)时,这种技术是合适的。 如果创建单例是昂贵的或具有不必要的副作用,那么您不希望使用free-for-all算法。

每个线程创建对象; 因为它认为还没有人创造它。 但是在InterlockedCompareExchange期间,只有一个线程真的能够设置全局单例。

奖金阅读

  • 一次性初始化辅助函数使您不必自己编写所有这些代码。 它们处理所有同步和内存障碍问题,并支持单人获取初始化和免费初始化模型。
  • .NET的延迟初始化原语提供了相同的C#版本。

这不是线程安全的。

你需要一个锁来将if()Interlocked.CompareExchange()放在一起,然后你就不再需要CompareExchange了。

你仍然有一个问题,你很可能正在创建并丢弃你的单身实例。 执行Interlocked.CompareExchange() ,无论赋值是否成功,都将始终执行Singleton构造函数。 因此,与你说的相比,你并没有好过(或者更糟糕的是,恕我直言):

 if ( _instance == null ) { lock(latch) { _instance = new Singleton() ; } } 

与交换lock的位置和测试为null的情况相比,线程争用的性能更好,但存在构造额外实例的风险。

.NET的一个明显的单例实现?

自动属性初始化(C#6.0)似乎不会导致您看到的Singleton的多个实例化。

 public class Singleton { static public Singleton Instance { get; } = new Singleton(); private Singleton(); } 

我认为.NET 4.0之后最简单的方法是使用System.Lazy

 public class Singleton { private static readonly Lazy lazy = new Lazy(() => new Singleton()); public static Singleton Instance { get { return lazy.Value; } } private Singleton() { } } 

Jon Skeet 在这里有一篇很好的文章,涵盖了很多实现单例的方法和每个方法的问题。

不要使用锁定。 使用您的语言环境

大多数简单的线程安全实现是:

 public class Singleton { private static readonly Singleton _instance; private Singleton() { } static Singleton() { _instance = new Singleton(); } public static Singleton Instance { get { return _instance; } } }