繁体   English   中英

为什么要使用双重锁定?

[英]Why Double-Checked Locking is used at all?

我继续运行使用双重检查锁定的代码,我仍然对它为什么会被使用感到困惑。

我最初不知道双重检查锁定是否被打破 ,当我学会它时,它为我放大了这个问题:为什么人们首先使用它? 是不是比较和交换更好?

if (field == null)
    Interlocked.CompareExchange(ref field, newValue, null);
return field;

(我的问题同时适用于C#和Java,尽管上面的代码是针对C#的。)

与原子操作相比,双重检查锁定是否具有某种固有优势?

与原子操作相比,双重检查锁定是否具有某种固有优势?

(这个答案只涉及C#;我不知道Java的内存模型是什么样的。)

主要区别在于潜在的种族。 如果你有:

if (f == null)
    CompareExchange(ref f, FetchNewValue(), null)

然后可以在不同的线程上任意多次调用FetchNewValue()。 其中一个主题赢得了比赛。 如果FetchNewValue()非常昂贵,并且您希望确保仅调用一次,那么:

if (f == null)
    lock(whatever)
        if (f == null)
            f = FetchNewValue();

保证FetchNewValue只被调用一次。

如果我个人想要进行低锁懒惰初始化,那么我按照你的建议行事:我使用一个互锁操作并忍受罕见的竞争条件,其中两个线程都运行初始化器,只有一个获胜。 如果这是不可接受的,那么我使用锁。

在C#中,它从未被打破过,所以我们现在可以忽略它。

您发布的代码假定newValue已经可用,或者是(重新)计算。 在双重检查锁定中,您可以保证只有一个线程实际执行初始化。

尽管如此,在现代C#中,我通常更喜欢使用Lazy<T>来处理初始化。

当锁定整个方法时遇到的性能下降很严重时,使用双重检查锁定。 换句话说,如果您不希望在对象(调用方法)或类上进行同步,则可以使用双重检查锁定。

如果存在很多争用锁并且由锁保护的资源创建成本高,则可能是这种情况; 我们希望将创建过程推迟到需要之前。 双重检查锁定通过首先验证条件(锁定提示)来帮助确定是否必须获得锁定来提高性能。

在Java 5中,当引入新的内存模型时,双重检查锁定在Java中被破坏。 在那之前,锁定提示很可能在一个线程中为true,而在另一个线程中为false。 在任何情况下, Initialization-on-Demand-Holder惯用法都是双重检查锁定模式的合适替代品; 我发现这更容易理解。

好吧,我想到的唯一优势是(幻觉)性能:以非线程安全的方式检查,然后执行一些锁定操作来检查变量,这可能很昂贵。 然而,由于双重检查的锁定方式被打破,从而排除了非线程安全检查的任何确定结论,并且它总是对我过早优化,我会声称不,没有优势 - 它是一个过时的预Java天成语 - 但很想得到纠正。

编辑:要清楚(呃),我相信双重检查锁定是一种习惯用法,每次都会在锁定和检查时发展为性能增强,并且大致与非封装的比较和交换接近相同的东西。 我个人也是封装代码的同步部分的粉丝,所以我认为调用另一个操作来做脏工作更好。

在某种程度上“有意义”的是,只有在启动时才能更改的值不需要被锁定才能被访问,但是你应该添加一些锁定(你可能根本不需要),以防万一线程尝试在启动时访问它,它大部分时间都可以工作。 它坏了,但我可以看出为什么它容易陷入困境。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM