c# - 具有 volatile 或内存屏障的双锁

标签 c# .net multithreading volatile memory-barriers

这是我现有的代码,运行了几个月没有出现任何问题。

public sealed class Singleton 
{
    private static Singleton value;
    private static object syncRoot = new Object();
    public static Singleton Value 
    {
        get 
        {
            if (Singleton.value == null) 
            {
                lock (syncRoot) 
                {
                    if (Singleton.value == null) 
                    {
                        Singleton.value = new Singleton();
                    }
                }
            }
            return Singleton.value;
        }
    }      
}

但是,我遇到了这个 link它概述了上述问题。

a) 写入 Singleton.value = new Singleton(); 可能会缓存在处理器上,因此其他线程可能最终看不到它。要修复此 volatile 使用关键字。

Q(1):C# lock 关键字不会处理这个问题吗?

b) 在同一篇文章中概述的另一个更好的解决方案是避免 volatile 并在写入后引入 System.Threading.Thread.MemoryBarrier(); Singleton.value

问题:

Q(2) 我不太明白写入后需要MemoryBarrier()。什么可能的重新排序可能会导致其他线程将 Singleton.value 视为 null ? lock 甚至可以防止其他线程读取任何内容。

Q(3) 屏障只会维持秩序,但如果值仍然是从某个缓存中读取的呢?是否仍然需要 volatile ?

Q(4) 自从 C# lock 本身放置它以来,真的需要屏障吗?

最后, 我是否需要使用这两种方法更新我的代码,或者它是否足够好?

编辑 有一个答案建议使用 Lazy 初始化。我知道了。

但是他们试图使用锁不能保证的 volatie 和 memorybarrier 来完成什么?

最佳答案

This is the code I have in place, running for couple of months with no issues.

如果有十亿分之一的失败几率,并且代码每天在一千台机器上运行一千次,那么平均每三年就会出现一次无法调试的严重故障。

如果它只在特定硬件上失败,而您在 x86 上进行所有测试,那么您将永远不会看到失败。

没有测试低锁代码正确性这样的事情。代码要么被证明是正确的,要么不是。您不能依赖测试。

Doesn't the C# lock keyword take care of this?

锁在其中一个读取上被删除。

The lock prevents other threads from even reading anything.

锁在其中一个读取上被删除。

Barriers will just maintain order but what if the value is still read from some cache instead. Isn't volatile still required ?

从缓存中读取相当于在时间上向后移动读取;由易变或明确的障碍引起的障碍对如何观察这种倒退运动施加了限制。

Is barrier really required there since C# lock itself places it ?

锁在其中一个读取上被删除。

Do I need to update my code with either approach or is it good enough?

我永远不会写这样的代码。如果需要延迟初始化,请使用 Lazy<T> .如果您需要单例,请使用单例模式的标准实现。不要自己解决这些问题;让专家为您解决这些问题。

But what were they trying to accomplish using volatile and memorybarrier that lock doesn't guarantee ?

他们试图正确地消除锁,从而在非竞争路径中节省几纳秒。与难以调试的罕见严重故障的成本相比,这些纳秒对您的用户来说有多大值(value)?

任何时候你试图删除一个锁,你都完全沉浸在低级内存模型的疯狂世界中。您必须假设所有内存都在不断变化,除非有什么东西使它保持静止;您必须假设内存访问的任何和所有合法重新排序都是可能的,甚至是大多数硬件上不可能的。您不知道将来会发明什么奇怪的硬件来运行您的代码。

不要去那里。我不喜欢使用线程;如果我想并行化某些东西,我的首选是使用虚拟机、容器或进程来解决问题。如果你必须使用线程,尽量不要共享内存。如果您必须共享内存,请使用专家构建的最高级别构造,例如 TaskLazy ,而不是自己推出内存障碍和互锁操作。

关于c# - 具有 volatile 或内存屏障的双锁,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/46308635/

相关文章:

c# - 扩展可以访问同一程序集中的内部方法并被外部使用吗?

C#,我怎么知道我的所有线程何时完成?

c# - 我应该使用 Unity(它还活着吗)?

c# - 集合上的动态 LINQ?

c# - 比较对象?

c# - 在 parse.com 上运行 linq 时, "non-filtering clauses"是什么意思?

c# - 使用 Catch block 返回,坏主意?

c# - 我需要在递归算法期间停止执行

java - 使用多个线程访问一个数组,但只能读取或只能写入

c# - 如何通过表单在 ASP.NET Web API 中实现 Active Directory 身份验证?