簡體   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