2008-12-27 100 views

回答

23

Implementing the Singleton Pattern in C#在第三個版本中討論了這個問題。

它說:

製作實例變量的波動使它發揮作用,如將外顯記憶障礙呼籲,雖然在後一種情況下究竟需要哪些障礙,甚至專家們也不會同意的。我傾向於嘗試避免專家不同意正確和錯誤的情況!

作者似乎暗示雙鎖定不太可能比其他策略工作,因此不應該使用。

+0

文章的新位置是:http://csharpindepth.com/Articles/General/Singleton.aspx – dotnetguy 2015-01-29 03:55:01

7

注意比在Java中(也很可能在.Net中),對於單例初始化的雙重檢查鎖定是完全不必要的,也是破壞的。由於類在初次使用之前不會被初始化,因此已經實現了所需的延遲初始化;

private static Singleton instance = new Singleton(); 

除非你的Singleton類包含的東西像第一次使用Singleton實例之前可能訪問的常量,這是所有你需要做的。

+1

DCL沒有工作,因爲Java 5中(見喬恩斯基特的評論,但他沒有談論究竟你必須做什麼才能使它工作)。您需要:1. Java 5. 2. DCL引用聲明爲volatile(或以某種方式原子化,例如使用AtomicReference)。 – 2008-12-27 12:14:48

+1

請參閱http://www.cs.umd.edu/~pugh/java/memoryModel/DoubleCheckedLocking.html中的「在新的Java內存模型下」部分。 – 2008-12-27 12:16:08

+0

對於Java Singleton和DCL模式,請參閱此鏈接http:// blogs .sun.com/cwebster/entry/double_check_locking – 2009-08-01 22:27:12

67

雙重檢查鎖定現在可以在Java和C#中工作(Java內存模型已更改,這是其中一種效果)。但是,你必須得到它正好正確。如果你把事情搞得一團糟,你最終可能會失去線程安全。

正如其他答案所述,如果您正在實施singleton pattern有更好的方法來做到這一點。就個人而言,如果我處於必須在雙重鎖定鎖定和「每次鎖定」代碼之間進行選擇的情況,那麼每次都會鎖定,直到我得到確實證據表明它正在造成瓶頸。當涉及到線程時,一個簡單明顯正確的模式是值得的。

+1

@Jon:來自問題的文章指出`volatile'有助於JDK5 +。什麼關於.NET?足夠'volatile'來實現對.NET 1.1的適當的雙重檢查鎖定? (例如,你的文章中的例子「第三個版本 - 嘗試使用雙重檢查鎖定的線程安全性」是什麼意思?當volatile屬性放在instance instance字段時,它在技術上是'固定的') – IgorK 2010-03-24 10:49:43

+9

@IgorK:是,我相信*它在變量被標記爲易失性時起作用。 – 2010-03-24 11:12:15

20

.NET 4.0有一個新類型:Lazy<T>,它取消了任何有關使模式錯誤的疑慮。它是新的任務並行庫的一部分。

請參閱MSDN並行計算開發中心:http://msdn.microsoft.com/en-us/concurrency/default.aspx

BTW,有一個補丁包(我認爲這是不支持的)可用here .NET 3.5 SP1。

-5

我已經得到了雙重檢查鎖定用布爾(即使用一個原始的,以避免懶初始化)工作:

private static Singleton instance; 
private static boolean created; 
public static Singleton getInstance() { 
    if (!created) { 
     synchronized (Singleton.class) { 
      if (!created) { 
       instance = new Singleton(); 
       created = true; 
      } 
     } 
    } 
    return instance; 
} 
2

我已經得到了雙重檢查鎖定由工作使用布爾值(即使用基元來避免延遲初始化):

使用布爾值的單例不起作用。除非您經歷內存障礙,否則不保證不同線程之間的操作順序。 換句話說,從第二個線程看到, created = true可之前instance= new Singleton();

0

我不明白爲什麼還有的實現模式上的雙重檢查鎖定一堆執行(這顯然是爲了解決編譯器的特質各種語言)。關於這一主題的維基百科文章顯示了天真方法和可能的方式來解決這個問題,但沒有一個是這麼簡單(在C#):

public class Foo 
{ 
    static Foo _singleton = null; 
    static object _singletonLock = new object(); 

    public static Foo Singleton 
    { 
    get 
    { 
     if (_singleton == null) 
     lock (_singletonLock) 
      if (_singleton == null) 
      { 
      Foo foo = new Foo(); 

      // Do possibly lengthy initialization, 
      // but make sure the initialization 
      // chain doesn't invoke Foo.Singleton. 
      foo.Initialize(); 

      // _singleton remains null until 
      // object construction is done. 
      _singleton = foo; 
      } 
     return _singleton; 
    } 
    } 

在Java中,你會使用同步(),而不是lock(),但它基本上是一樣的想法。如果單態字段被賦值時可能存在不一致性,那麼爲什麼不首先使用本地作用域變量,然後在退出臨界區之前的最後時刻分配單態字段?我錯過了什麼嗎?

@ michael-borgwardt提出的論點是,在C#和Java中,靜態字段只在第一次使用時初始化一次,但行爲是語言特定的。我經常使用這種模式進行集合屬性的懶化初始化(例如user.Sessions)。

0

我不明白爲什麼所有的人都說雙重鎖定是不好的模式,但不適應代碼使其正常工作。在我看來,這下面的代碼應該工作得很好。

如果有人能告訴我,如果這段代碼遭受了卡梅隆文章中提到的問題,請做。

public sealed class Singleton { 
    static Singleton instance = null; 
    static readonly object padlock = new object(); 

    Singleton() { 
    } 

    public static Singleton Instance { 
     get { 
      if (instance != null) { 
       return instance; 
      } 

      lock (padlock) { 
       if (instance != null) { 
        return instance; 
       } 

       tempInstance = new Singleton(); 

       // initialize the object with data 

       instance = tempInstance; 
      } 
      return instance; 
     } 
    } 
} 
相關問題