2011-05-15 75 views
1

考慮下面的代碼:此圖案的名稱? (答案:延遲初始化有雙重檢查鎖定)

public class Foo 
{ 
    private static object _lock = new object(); 

    public void NameDoesNotMatter() 
    { 
     if(SomeDataDoesNotExist()) 
     { 
      lock(_lock) 
      { 
       if(SomeDataDoesNotExist()) 
       { 
        CreateSomeData(); 
       } 
       else 
       { 
        // someone else also noticed the lack of data. We 
        // both contended for the lock. The other guy won 
        // and created the data, so we no longer need to. 
        // But once he got out of the lock, we got in. 
        // There's nothing left to do. 
       } 
      } 
     } 
    } 

    private bool SomeDataDoesNotExist() 
    { 
     // Note - this method must be thread-safe. 
     throw new NotImplementedException(); 
    } 

    private bool CreateSomeData() 
    { 
     // Note - This shouldn't need to be thread-safe 
     throw new NotImplementedException(); 
    } 
} 

首先,有一些假設,我需要狀態:

  1. 有一個很好的理由我不能一旦創建應用程序,就不要這樣做。也許數據還沒有提供等等。

  2. Foo可以被實例化,並且可以從兩個或多個線程同時使用。我希望其中一個最終創建一些數據(但不是兩個),然後我將允許兩個訪問相同的數據(忽略訪問數據的線程安全)

  3. SomeDataDoesNotExist()的成本是不是很大。

現在,這不一定侷限於某些數據創建情況,但這是我能想到的一個例子。

我特別感興趣的是識別模式的部分是check - > lock - > check。我不得不多次向開發者解釋這種模式,他們一開始並沒有得到算法,但可以欣賞它。

無論如何,其他人必須做類似的事情。這是一種標準化模式嗎?它叫什麼?

+0

看起來像一個奇怪的方式來利用同步,[懶惰加載](http://en.wikipedia.org/wiki/Lazy_loading),[單身模式](http://en.wikipedia.org/wiki/Singleton_pattern)給我。 – 2011-05-15 20:39:17

+0

你在暗示可能有更好但功能相同的方法來做到這一點。你有什麼特別的想法? – 2011-05-15 20:42:27

+0

我說的很奇怪,因爲它將許多模式組合成一種方法。通常我會希望看到他們在單獨的方法。即延遲加載的單例(數據)將在單獨的方法/屬性中。 – 2011-05-15 20:45:46

回答

9

使用雙重檢查鎖定的惰性初始化?

+2

準確地。順便說一下,.NET 4提供了一個非常好的'Lazy'類來處理Lazy初始化的實現細節。有關示例,請參閱http://stackoverflow.com/questions/5986466/why-use-system-runtime-caching-or-system-web-caching-vs-static-variables/5986819#5986819。 – StriplingWarrior 2011-05-15 20:43:26

+0

我要去看看這個懶惰類。我很好奇,如果它在處理鎖定方面是線程安全的。 – 2011-05-15 20:51:57

+0

下面是關於術語Ben Voigt用於鎖定的維基文章:http://en.wikipedia.org/wiki/Double-checked_locking – 2011-05-15 21:14:22

0

唯一想到這種類型的名稱是「斷層」。這個名字在iOS Core-Data框架中用於類似的效果。

基本上,你的方法NameDoesNotMatter是故障,每當有人調用它,它導致的對象來獲取填充或初始化。

有關如何使用此設計模式的更多詳細信息,請參閱http://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/CoreData/Articles/cdFaultingUniquing.html

+0

有趣。 Objective-C中這種斷層是否是線程安全的?或者我們可以說我的是一個線程安全的故障? – 2011-05-15 20:44:20

+0

由於故障發生而造成的新對象的斷層和創建是線程安全的AFAIK,但僅限於「上下文」內。 – sinha 2011-05-15 20:51:39

4

我特別感興趣識別模式的部分是check - > lock - > check。

這就是double-checked locking

請注意,在較早的Java版本(Java 5之前)中,由於定義了Java內存模型,因此它並不安全。在Java 5中,對Java的內存模型的規範進行了更新,以便它現在是安全的。

+0

我們走了,這就是我以後的事。我只是沒有一個詞彙。我試圖接受這個答案,但我是新的,給我一秒鐘。 – 2011-05-15 20:49:09

+0

當網站實際上允許我投票給人時,我會在路上投票。謝謝你的時間。 – 2011-05-15 21:15:44

13

雖然我可以看到你怎麼會認爲這看起來像是雙重檢查鎖定,但它實際上看起來像是危險地斷開和不正確的雙重檢查鎖定。如果沒有對SomeDataDoesNotExist和CreateSomeData的實際實現進行批判,我們不能保證這件事實際上在每個處理器上都是線程安全的。

對於雙重檢查鎖定是如何出錯的分析的例子,看看這個破碎的和不正確的版本雙重檢查鎖定的:

C# manual lock/unlock

我的建議是:不使用任何沒有令人信服的理由的低鎖定技術以及內存模型專家的代碼審查;你可能會錯誤的。大多數人都會。

特別是,不使用雙重檢查鎖定,除非你能描述什麼內存訪問重新排序的處理器可以代表你做,並提供令人信服的論據,你的解決方案是正確的給予任何可能的內存訪問重新排序。當你從一個已知的正確的實現中稍微離開時,你需要從頭開始分析。你不能認爲只是因爲一個雙重檢查鎖定的實現是正確的,他們都是;幾乎沒有一個是正確的。

+0

這是一個很好的觀點,埃裏克。在我的真實代碼中,SomeDataDoesNotExist()完全是線程安全的(我將避免提到爲什麼可能會導致我們的切線)。如果不是,那將是一個大問題。該鏈接很有幫助。我相信我很安全,但我會重新閱讀這個鏈接幾次以確保。 – 2011-05-15 23:19:34

+0

我只是通過在最後兩個方法中添加註釋來修改我的原始帖子。 – 2011-05-15 23:24:30

+0

我還會考慮使用更高級別的構造可以更簡單地完成我對低級鎖的操作。 – 2011-05-15 23:26:11