我在Jon的Skeet在线页面上读到了如何在C#中创建一个线程安全的Singleton
http://csharpindepth.com/Articles/General/Singleton.aspx
// Bad code! Do not use!
public sealed class Singleton
{
private static Singleton instance=null;
private Singleton()
{
}
public static Singleton Instance
{
get
{
if (instance==null)
{
instance = new Singleton();
}
return instance;
}
}
}
Run Code Online (Sandbox Code Playgroud)
在此代码下面的段落中,它说:
如前所述,上述内容不是线程安全的.两个不同的线程都可以评估测试if(instance == null)并发现它是真的,然后两个创建实例,这违反了单例模式.请注意,实际上可能已经在计算表达式之前创建了实例,但是内存模型不保证其他线程可以看到实例的新值,除非已经传递了合适的内存屏障.
你能否解释为什么内存模型不能保证其他线程看不到实例的新值?
静态变量位于堆上,但为什么不立即与其他线程共享?我们是否需要等待上下文切换,以便其他线程知道实例不再为空?
Jon*_*eet 14
你能否解释为什么内存模型不能保证其他线程看不到实例的新值?
内存模型很复杂,目前还没有非常明确的文档记录,但从根本上来说,很少有情况可以安全地依赖于一个线程在另一个线程上"看到"而没有某些锁定或其他线程间的线程所写的值沟通继续.
例如,考虑一下:
// Bad code, do not use
public class BigLoop
{
private static bool keepRunning = true;
public void TightLoop()
{
while (keepRunning)
{
}
}
public void Stop()
{
keepRunning = false;
}
}
Run Code Online (Sandbox Code Playgroud)
如果你创建了两个线程,它调用一个TightLoop而另一个来电Stop,也不能保证该循环方法将永远终止.
现代CPU中有许多级别的缓存,并且要求每次读取都返回到主内存将消除大量优化.所以我们有内存模型,可以保证哪些变化在什么情况下肯定是可见的.除了这些保证之外,允许JIT编译器假设实际上只有一个线程 - 因此它可以将字段的值缓存在寄存器中,并且永远不会再次访问主存储器,例如.
当前记录的内存模型严重不足,并且表明一些明显奇怪的优化应该是有效的.我不会去太远了这条路线,但它是值得一读的乔·达菲的博客文章CLR 2.0内存模型.(这比记录的ECMA内存模型更强大,但是博客文章并不是这类关键文档的理想位置,我认为仍然需要更清晰.)
静态变量位于堆上,但为什么它不与其他线程共享?
这是与其他线程共享-但价值并不一定会立即可见.