C# 一个简单的单例实现
这难道不是一种更简单、更安全(因此更好)的实现单例的方法,而不是执行双重检查锁定的mambo jambo吗?这种方法有什么缺点吗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 {
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; }
}
}