C# 一个简单的单例实现

C# 一个简单的单例实现,c#,.net,multithreading,singleton,C#,.net,Multithreading,Singleton,这难道不是一种更简单、更安全(因此更好)的实现单例的方法,而不是执行双重检查锁定的mambo jambo吗?这种方法有什么缺点吗 public class Singleton { private static Singleton _instance; private Singleton() { Console.WriteLine("Instance created"); } public static Singleton Instance {

这难道不是一种更简单、更安全(因此更好)的实现单例的方法,而不是执行双重检查锁定的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() { }
}
编辑:线程安全测试失败,有人能解释原因吗?为什么Interlocated.CompareExchange不是真正的原子


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

如果你的单身汉有多次初始化自己的危险,你会遇到更糟糕的问题。为什么不直接使用:

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

  public static Singleton Instance{get{return instance;}}
}
在初始化方面绝对线程安全


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

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

这不是线程安全的


您需要一个锁来将
if()
联锁在一起。CompareExchange()
,这样您就不再需要
CompareExchange

您仍然存在一个问题,即您很可能正在创建并丢弃单例实例。执行
Interlocked.CompareExchange()
时,无论赋值是否成功,都将始终执行
Singleton
构造函数。因此,如果你说:

if ( _instance == null )
{
  lock(latch)
  {
    _instance = new Singleton() ;
  }
}
与交换
锁的位置和null测试相比,线程争用的性能更好,但要冒构造额外实例的风险。

这是怎么回事

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();
    }
}
这是本页的第五个版本:


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

为了不使用“双重检查锁定mambo jambo”或只是不实现自己的单例程序,请使用.NET 4.0-中包含的现成解决方案。

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

无锁编程

,在他的书《Windows上的并发编程》中,实际上分析了这一模式,这正是您在第10章“内存模型和锁自由”第526页中尝试使用的模式

他将此模式称为松弛引用的惰性初始化:

public class LazyInitRelaxedRef<T> where T : class
{
    private volatile T m_value;
    private Func<T> m_factory;

    public LazyInitRelaxedRef(Func<T> factory) { m_factory = factory; }


    public T Value
    {
        get
        {
            if (m_value == null) 
              Interlocked.CompareExchange(ref m_value, m_factory(), null);
            return m_value;
        }
    }

    /// <summary>
    /// An alternative version of the above Value accessor that disposes
    /// of garbage if it loses the race to publish a new value.  (Page 527.)
    /// </summary>
    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;
        }
    }
}
public类LazyInitRelaxedRef其中T:class
{
私有挥发性T m_值;
私营Func m_工厂;
公共LazyInitRelaxedRef(Func工厂){m_工厂=工厂;}
公共价值
{
得到
{
如果(m_值==null)
Interlocated.CompareExchange(参考m_值,m_工厂(),null);
返回m_值;
}
}
/// 
///上述值存取器的替代版本,用于
///如果它在发布新值的竞争中失败,它将被丢弃(第527页)
/// 
不动产抵押的公共价值
{
得到
{
如果(m_值==null)
{
T obj=m_工厂();
if(联锁的比较交换(参考m_值,obj,null)!=null且obj可识别)
((IDisposable)obj.Dispose();
}
返回m_值;
}
}
}
正如我们所看到的,在上面的示例中,方法是无锁的,代价是创建一次性对象。在任何情况下,对于此类API的使用者,Value属性都不会更改

平衡权衡

锁自由是有代价的,这是一个仔细选择权衡的问题。在这种情况下,锁定自由的代价是必须创建不打算使用的对象的实例。这可能是一个可以接受的代价,因为您知道,通过无锁,死锁和线程争用的风险更低

然而,在这个特定的实例中,单例的语义本质上是创建一个对象的单例,因此我宁愿选择@Centro在其回答中引用的

然而,它仍然回避了一个问题,我们应该在什么时候使用
联锁的.compareeexchange
?我喜欢你的例子,它很发人深省,很多人很快就把它当成错误,但它并不像“盲目引用”那样错误得可怕

归根结底,这取决于您是否计算了权衡并决定:

  • 只生成一个实例有多重要
  • 无锁有多重要

只要您知道折衷,并有意识地决定创建新对象以获得无锁的好处,那么您的示例也可以是一个可接受的答案

您的单例初始值设定项的行为与它应该的完全一样。见陈雷蒙的:

这是一个双重检查锁,但没有锁定。在进行初始构造时,我们不需要锁定,而是让创建对象的所有人都可以自由使用它。如果五个线程都同时到达此代码,那么当然,让我们创建五个对象。在每个人都创建了他们认为是赢家的对象之后,他们称之为Interlocked-Compare-Exchange-Pointer-Release,试图更新全局指针

当允许多个线程尝试创建单线程(并让所有的失败者销毁他们的副本)时,这种技术是合适的。如果创建单例是昂贵的或没有
public class LazyInitRelaxedRef<T> where T : class
{
    private volatile T m_value;
    private Func<T> m_factory;

    public LazyInitRelaxedRef(Func<T> factory) { m_factory = factory; }


    public T Value
    {
        get
        {
            if (m_value == null) 
              Interlocked.CompareExchange(ref m_value, m_factory(), null);
            return m_value;
        }
    }

    /// <summary>
    /// An alternative version of the above Value accessor that disposes
    /// of garbage if it loses the race to publish a new value.  (Page 527.)
    /// </summary>
    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;
        }
    }
}
public class Singleton
{
    private static readonly Lazy<Singleton> lazy = new Lazy<Singleton>(() => new Singleton());

    public static Singleton Instance { get { return lazy.Value; } }

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

    private Singleton() { }

    static Singleton()
    {
        _instance = new Singleton();
    }

    public static Singleton Instance
    {
        get { return _instance; }
    }
}