.NET 中的双重检查锁定
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/394898/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Double-checked locking in .NET
提问by erikkallen
采纳答案by Cameron MacFarland
Implementing the Singleton Pattern in C#talks about this problem in the third version.
在 C# 中实现单例模式在第三个版本中谈到了这个问题。
It says:
它说:
Making the instance variable volatile can make it work, as would explicit memory barrier calls, although in the latter case even experts can't agree exactly which barriers are required. I tend to try to avoid situations where experts don't agree what's right and what's wrong!
使实例变量 volatile 可以使其工作,就像显式内存屏障调用一样,尽管在后一种情况下,即使专家也不能确切地同意需要哪些屏障。我倾向于尽量避免专家不同意什么是对什么是错的情况!
The author seems to imply that double locking is less likely to work than other strategies and thus should not be used.
作者似乎暗示双重锁定比其他策略更不可能奏效,因此不应使用。
回答by Jon Skeet
Double-checking locking now works in Java as well as C# (the Java memory model changed and this is one of the effects). However, you have to get it exactlyright. If you mess things up even slightly, you may well end up losing the thread safety.
双重检查锁定现在可以在 Java 和 C# 中使用(Java 内存模型发生了变化,这是影响之一)。但是,您必须完全正确。如果你稍微搞砸了,你很可能最终会失去线程安全性。
As other answers have stated, if you're implementing the singleton patternthere are much better ways to do it. Personally, if I'm in a situation where I have to choose between double-checked locking and "lock every time" code I'd go for locking every time until I'd got real evidence that it was causing a bottleneck. When it comes to threading, a simple and obviously-correct pattern is worth a lot.
正如其他答案所述,如果您正在实施单例模式,则有更好的方法来做到这一点。就我个人而言,如果我必须在双重检查锁定和“每次锁定”代码之间做出选择,我会每次都进行锁定,直到我得到真正的证据表明它会导致瓶颈。当涉及到线程时,一个简单且明显正确的模式很有价值。
回答by David Messner
.NET 4.0 has a new type: Lazy<T>that takes away any concern about getting the pattern wrong. It's part of the new Task Parallel Library.
.NET 4.0 有一个新类型:Lazy<T>它消除了对错误模式的任何担忧。它是新任务并行库的一部分。
See the MSDN Parallel Computing Dev Center: http://msdn.microsoft.com/en-us/concurrency/default.aspx
请参阅 MSDN 并行计算开发中心:http: //msdn.microsoft.com/en-us/concurrency/default.aspx
BTW, there's a backport (I believe it is unsupported) for .NET 3.5 SP1 available here.
顺便说一句,还有一个补丁包(我认为这是不支持的)可用的.NET 3.5 SP1在这里。
回答by Michael Borgwardt
Note than in Java (and most likely in .Net as well), double-checked locking for singleton initialization is completely unnecessary as well as broken. Since classes are not initialized until they're first used, the desired lazy initialization is already achieved by this;
请注意,在 Java 中(最有可能在 .Net 中也是如此),单例初始化的双重检查锁定是完全没有必要的,而且已经被破坏了。由于类在第一次使用之前不会被初始化,因此已经实现了所需的延迟初始化;
private static Singleton instance = new Singleton();
Unless your Singleton class contains stuff like constants that may be accessed before a Singleton instance is first used, this is all you need to do.
除非您的 Singleton 类包含可以在首次使用 Singleton 实例之前访问的常量之类的东西,否则这就是您需要做的全部。
回答by M.Parent
I don't get why all people says that the double-check locking is bad pattern, but don't adapt the code to make it work correctly. In my opinion, this below code should work just fine.
我不明白为什么所有人都说双重检查锁定是不好的模式,但不要修改代码以使其正常工作。在我看来,下面的代码应该可以正常工作。
If someone could tell me if this code suffer from the problem mentionned in Cameron's article, please do.
如果有人能告诉我这段代码是否遇到了 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;
}
}
}
回答by Dimafa
I've gotten double-checked locking to work by using a boolean (i.e. using a primitive to avoid the lazy initialisation):
我通过使用布尔值(即使用原语来避免延迟初始化)获得了双重检查锁定:
The singleton using boolean does not work. The order of operations as seen between different threads is not guaranteed unless you go through a memory barrier.
In other words, as seen from a second thread,
created = truemay be executed before instance= new Singleton();
使用布尔值的单例不起作用。除非您通过内存屏障,否则无法保证在不同线程之间看到的操作顺序。换句话说,从第二个线程来看,
created = true可能会在之前执行instance= new Singleton();
回答by Erhhung
I don't quite understand why there are a bunch of implementation patterns on double-checked locking (apparently to work around compiler idiosyncrasies in various languages). The Wikipedia article on this topic shows the naive method and the possible ways to solve the problem, but none are as simple as this (in C#):
我不太明白为什么有一堆关于双重检查锁定的实现模式(显然是为了解决各种语言的编译器特性)。关于这个主题的维基百科文章显示了解决问题的幼稚方法和可能的方法,但没有一个像这样简单(在 C# 中):
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;
}
}
In Java, you'd use synchronized() instead of lock(), but it's basically the same idea. If there's the possible inconsistency of when the singleton field gets assigned, then why not just use a locally scoped variable first and then assign the singleton field at the last possible moment before exiting the critical section? Am I missing something?
在 Java 中,您将使用 synchronized() 而不是 lock(),但这基本上是相同的想法。如果分配单例字段的时间可能存在不一致,那么为什么不首先使用局部范围的变量,然后在退出临界区之前的最后一刻分配单例字段?我错过了什么吗?
There's the argument by @michael-borgwardt that in C# and Java the static field only gets initialized once on first use, but thatbehavior is language specific. And I've used this pattern frequently for lazy initialization of a collection property (e.g. user.Sessions).
@michael-borgwardt 提出的论点是,在 C# 和 Java 中,静态字段仅在首次使用时初始化一次,但该行为是特定于语言的。我经常使用这种模式来延迟初始化集合属性(例如 user.Sessions)。
回答by Jono
I've gotten double-checked locking to work by using a boolean (i.e. using a primitive to avoid the lazy initialisation):
我通过使用布尔值(即使用原语来避免延迟初始化)获得了双重检查锁定:
private static Singleton instance;
private static boolean created;
public static Singleton getInstance() {
if (!created) {
synchronized (Singleton.class) {
if (!created) {
instance = new Singleton();
created = true;
}
}
}
return instance;
}

