.net NET中的双重检查锁定
我看到一篇文章讨论了为什么双重检查锁定范式在Java中被打破。如果变量声明为.net NET中的双重检查锁定,.net,multithreading,paradigms,double-checked-locking,.net,Multithreading,Paradigms,Double Checked Locking,我看到一篇文章讨论了为什么双重检查锁定范式在Java中被打破。如果变量声明为volatile,在第三个版本中讨论了这个问题,那么这个范例对.NET(特别是C#)有效吗 它说: 使实例变量变为volatile可以使其工作,就像显式内存障碍调用一样,尽管在后一种情况下,即使专家也不能确切地同意需要哪些障碍。我倾向于避免专家不同意什么是对的什么是错的情况 作者似乎暗示,与其他策略相比,双重锁定不太可能起作用,因此不应使用。注意,与Java(以及最有可能的.Net)相比,用于单例初始化的双重检查锁定是完
volatile
,在第三个版本中讨论了这个问题,那么这个范例对.NET(特别是C#)有效吗
它说:
使实例变量变为volatile可以使其工作,就像显式内存障碍调用一样,尽管在后一种情况下,即使专家也不能确切地同意需要哪些障碍。我倾向于避免专家不同意什么是对的什么是错的情况
作者似乎暗示,与其他策略相比,双重锁定不太可能起作用,因此不应使用。注意,与Java(以及最有可能的.Net)相比,用于单例初始化的双重检查锁定是完全不必要的,也是不必要的。因为类在第一次使用之前是不会初始化的,所以通过这种方式已经实现了所需的延迟初始化
private static Singleton instance = new Singleton();
除非您的Singleton类包含在首次使用Singleton实例之前可以访问的常量之类的内容,否则这就是您需要做的全部工作。双重检查锁定现在在Java和C#中都可以工作(Java内存模型发生了变化,这是影响之一)。然而,你必须完全正确。如果你把事情搞得一团糟,你很可能会失去线程的安全性
正如其他答案所述,如果您正在实施,那么有更好的方法来实现它。就我个人而言,如果我必须在自己实现双重检查锁定和“每次锁定”代码之间做出选择,我会每次都锁定,直到我得到真正的证据证明这是造成瓶颈的原因。当谈到线程时,一个简单且明显正确的模式是非常有价值的。.NET4.0有一种新类型:
Lazy
,它消除了任何关于模式出错的顾虑。它是新任务并行库的一部分
请参阅MSDN并行计算开发中心:
顺便说一句,有一个用于.NET 3.5 SP1的后端口(我相信它不受支持)。我通过使用布尔值(即使用原语避免延迟初始化)获得了双重检查锁定:
我已经通过使用布尔值(即使用原语来避免延迟初始化)实现了双重检查锁定: 使用布尔值的单例不起作用。除非您通过内存屏障,否则无法保证不同线程之间的操作顺序。 换句话说,从第二条线来看,
created=true
可以在instance=new Singleton()之前执行代码>我不太明白为什么在双重检查锁定上有一堆实现模式(显然是为了解决各种语言中的编译器特性)。Wikipedia关于这个主题的文章展示了天真的方法和解决问题的可能方法,但没有一个像这样简单(用C#):
在Java中,使用synchronized()而不是lock(),但基本上是相同的想法。如果分配单例字段的时间可能不一致,那么为什么不首先使用一个局部范围的变量,然后在退出临界段之前的最后一个可能时刻分配单例字段呢?我错过什么了吗
@michael borgwardt认为,在C#和Java中,静态字段只在第一次使用时初始化一次,但认为行为是特定于语言的。我经常使用这种模式对集合属性(例如user.Sessions)进行延迟初始化。我不明白为什么所有人都说双重检查锁定是一种糟糕的模式,但不调整代码使其正常工作。在我看来,下面的代码应该可以正常工作
如果有人能告诉我这段代码是否存在Cameron文章中提到的问题,请告诉我
public sealed class Singleton {
static Singleton instance = null;
static readonly object padlock = new object();
Singleton() {
}
public static Singleton Instance {
get {
if (instance != null) {
return instance;
}
lock (padlock) {
if (instance != null) {
return instance;
}
tempInstance = new Singleton();
// initialize the object with data
instance = tempInstance;
}
return instance;
}
}
}
DCL从Java5开始就可以工作(请参见JonSkeet的评论,尽管他并没有具体说明您必须做些什么才能使其工作)。你需要:1。爪哇5。2.DCL引用声明为volatile(或者在某种程度上是原子的,例如使用AtomicReference)。有关Java单例和DCL模式,请参阅中的“在新Java内存模型下”一节。请参阅此链接@Jon:问题中的文章指出,volatile
有助于JDK5+。那.NET呢?volatile
是否足以在.NET1.1上实现正确的双重检查锁定?(例如,您文章中的“第三个版本-尝试使用双重检查锁定的线程安全”示例如何:当volatile
放在instance
static字段上时,它在技术上是“修复”的吗?@IgorK:是的,我相信当变量标记为volatile时它会起作用。@Jon:好的,谢谢。至少对于单身人士来说,我们确实有更好的解决方案。@Myster:我个人总是会把锁取出来,尤其是当你做其他一些慢动作时,这使得锁的成本变得微不足道。@Myster:我说的“取下锁”是指“获得锁”。我发现它是不明确的:)是的,删除外部检查。双重检查锁定的问题是实例可能是非null的,但仍然没有初始化。因此,即使对象尚未初始化,同步块之外的线程也可以通过初始(未同步)null检查。上面的代码通过检查布尔值来解决这个问题。当created=true被命中时,我们知道实例已经初始化,因此同步块外的线程无法获得未初始化的实例。人们之所以说它不好,是因为它极易出错。我认为您的代码在实例
成员上需要一个volatile
。(至少在这种情况下,代码是否有效不是一个意见问题,而是一个事实问题。我认为事实是它不起作用,但我可能在这一点上错了)。@erikkallen没有必要在.NET中使用volatile。别忘了.NETH
public class Foo
{
static Foo _singleton = null;
static object _singletonLock = new object();
public static Foo Singleton
{
get
{
if ( _singleton == null )
lock ( _singletonLock )
if ( _singleton == null )
{
Foo foo = new Foo();
// Do possibly lengthy initialization,
// but make sure the initialization
// chain doesn't invoke Foo.Singleton.
foo.Initialize();
// _singleton remains null until
// object construction is done.
_singleton = foo;
}
return _singleton;
}
}
public sealed class Singleton {
static Singleton instance = null;
static readonly object padlock = new object();
Singleton() {
}
public static Singleton Instance {
get {
if (instance != null) {
return instance;
}
lock (padlock) {
if (instance != null) {
return instance;
}
tempInstance = new Singleton();
// initialize the object with data
instance = tempInstance;
}
return instance;
}
}
}