4

我繼續在使用雙重檢查鎖定的代碼上運行,並且我仍然對爲什麼使用它而感到困惑。爲什麼選擇雙重鎖定?

我最初並不知道double-checked locking is broken,當我知道它時,它爲我放大了這個問題:人們爲什麼首先使用它?不比較和交換更好?

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

(我的問題適用於C#和Java,但上面的代碼是C#)。

是否雙重檢查鎖定有某種先天優勢相比,原子操作?

+0

我不認爲你的問題可以有確切的答案,除了那些人可能沒有聽說過它被打破,而且雙重檢查鎖定是一個半顯而易見的天真的解決方案,以規避性能點擊同步... – 2011-05-23 04:17:16

+0

@Merlyn:但它仍然是壞的?我認爲它在某些版本的Java中已被修復(編輯:從JDK5開始,如果你也使用'volatile',它顯然是有效的),並且我仍然看到代碼... – Mehrdad 2011-05-23 04:18:27

+0

@Mehrdad:http://stackoverflow.com/問題/ 394898 /雙檢鎖式網/ 394932#394932。儘管如此,如果我有另一種選擇,我不會使用習慣用語來打破某些版本的「便攜式」語言。我不知道原子操作的性能(或者說關於那個級別的代碼,真的;)),但我認爲它比鎖定更好,並且可能允許多個線程繼續。 – 2011-05-23 04:21:43

回答

1

那麼,只有我認爲的好處是(的幻覺)性能:檢查非線程安全的方式,然後做一些鎖定操作來檢查變量,這可能是昂貴的。然而,由於雙重檢查鎖定被破壞的方式排除了非線程安全檢查的任何確定性結論,並且它總是對我造成過早的優化,所以我會聲稱不會,沒有任何優勢 - 這是一個過時的預先安排, Java天成語 - 但很想被糾正。

編輯:爲了清楚(呃),我相信雙重檢查鎖定是一種習慣,演變爲每次鎖定和檢查時的性能增強,並且大體上與非封裝比較接近於同樣的事物 - 和交換。不過,我個人也是封裝代碼的同步部分的粉絲,所以我認爲調用另一個操作來完成骯髒的工作會更好。

+0

即使它*是*正確的(我相信它是在最新版本的Java中,雖然我可能是錯的),會有什麼優勢嗎?如果/ CAS做同樣的事情,不會嗎? – Mehrdad 2011-05-23 04:17:32

+0

我不認爲有什麼好處,除非您可以爭辯說內聯操作比其他地方的呼叫更清晰,或者您擔心外部呼叫的性能影響?微不足道的,COBOL式的擔憂,恕我直言。 – Matt 2011-05-23 04:23:41

0

它在某種程度上「有意義」,只有在啓動時纔會改變的值不需要被鎖定以便被訪問,但是您應該添加一些鎖定(您可能不需要這些鎖定)以防萬一兩個線程在啓動時嘗試訪問它,並且它在大多數情況下都可用。 它的破碎,但我可以看到爲什麼它容易陷入陷阱。

4

在C#中,它從未被破壞,所以我們現在可以忽略它。

您發佈的代碼假定newValue已經可用,或者吱吱地(重新)計算。在雙重檢查鎖定中,您可以保證只有一個線程會實際執行初始化。但是,在現代C#中,我通常更喜歡使用Lazy<T>來處理初始化。

+0

+1哦,我首先感到困惑,但我明白你的意思了;是的,關於初始化便宜性的觀點非常重要,我根本沒有注意到這一點;謝謝! :) – Mehrdad 2011-05-23 04:30:11

+0

太棒了!這確實可能是一個問題。 – Matt 2011-05-23 04:50:21

+1

看到這個鏈接:http://en.wikipedia.org/wiki/Double-checked_locking#Usage_in_Microsoft_.NET_.28Visual_Basic.2C_C.23.29。我的讀書是DCL **在C#中被打破了,除非你使用明確的寫障礙或volatile。這當然不是如何實施原來的DCL成語......所以說「從未被打破」是誇大了事情。 – 2011-05-23 04:55:04

1

當在整個方法上進行鎖定時遇到性能下降很大時,會使用雙重檢查鎖定。換句話說,如果您不希望在對象(在其上調用該方法)或類上進行同步,則可以使用雙重檢查鎖定。

這可能是這種情況,如果存在大量的鎖爭用以及由鎖保護的資源創建成本高昂;人們希望推遲創作過程直到需要。通過首先驗證條件(鎖定提示)以幫助確定是否必須獲取鎖定,雙重檢查鎖定可提高性能。

直到Java 5引入新的內存模型時,Java中的雙重檢查鎖定才被破壞。在此之前,鎖提示很可能在一個線程中爲真,在另一個線程中爲假。在任何情況下,Initialization-on-Demand-Holder成語都是雙重檢查鎖定模式的合適替代品;我覺得這更容易理解。

11

與原子操作相比,雙重檢查鎖定是否具有某種內在優勢?

(這個答案只包括C#,我不知道Java的內存模型等。)

的主要區別是潛在的競爭。如果你有:

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

然後FetchNewValue()可以在不同的線程上任意多次調用。其中一個線索贏得了比賽。如果FetchNewValue()是極其昂貴的,你想確保它被稱爲只有一次,那麼:

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

保證FetchNewValue只調用一次。

如果我本人想要做一個低鎖定懶惰初始化,然後我做你的建議:我使用互鎖操作,並與兩個線程都運行初始化程序,只有一個勝利罕見的競爭條件生活。如果這是不可接受的,那麼我使用鎖。

相關問題