2014-03-28 23 views
2

我有我的單身如下:辛格爾頓在當前線程

public class CurrentSingleton 
{ 
    private static CurrentSingleton uniqueInstance = null; 
    private static object syncRoot = new Object(); 
    private CurrentSingleton() { } 

    public static CurrentSingleton getInstance() 
    { 
     if (uniqueInstance == null) 
     { 
      lock (syncRoot) 
      { 
       if (uniqueInstance == null) 
        uniqueInstance = new CurrentSingleton(); 
      } 
     } 
     return uniqueInstance; 
    } 
} 

我想檢查,如果我有兩個線程,會有兩種不同的單身?我想,我有兩個不同的單身人士(具有不同的引用),所以我在做什麼:

class Program 
{ 
    static void Main(string[] args) 
    { 
     int currentCounter = 0; 
     for (int i = 0; i < 100; i++) 
     { 
      cs1 = null; 
      cs2 = null; 

      Thread ct1 = new Thread(cfun1); 
      Thread ct2 = new Thread(cfun2); 
      ct1.Start(); 
      ct2.Start(); 
      if (cs1 == cs2) currentCounter++; 
     } 
     Console.WriteLine(currentCounter); 
     Console.Read(); 

    } 

    static CurrentSingleton cs1; 
    static CurrentSingleton cs2; 

    static void cfun1() 
    { 
     cs1 = CurrentSingleton.getInstance(); 
    } 

    static void cfun2() 
    { 
     cs2 = CurrentSingleton.getInstance(); 
    } 
} 

我想,我應該得到currentCounter = 0(在這種情況下,每兩個單是不同的 - 因爲通過創建其他threrad)。不幸的是,我得到了例如currentCounter = 70,所以在70例我有同樣的單身人士...你能告訴我爲什麼嗎?

回答

4

默認情況下,static場是訪問它的所有線程共享一個實例。

你應該看看[ThreadStatic] attribute。將其應用到static字段,以使每個訪問它的線程具有不同的實例。

+0

'[ThreadStatic]'可以工作,但請注意,存在奇怪的初始化問題(字段初始值設定項僅適用於訪問的第一個線程),並且沒有乾淨的方法如果需要處理實例,則處理清理。 –

+0

我在變量static CurrentSingleton cs1之前添加了[ThreadStatic];和靜態CurrentSingleton cs2 ;.現在我得到currentCounter = 100,所以在任何情況下,我都有相同的實例...它仍然是錯的 – makcis

+0

@makcis你需要它在CurrentSingleton類中,而不是你設置的變量。 –

9

我想檢查,如果我有兩個線程,會有兩種不同的單身

沒有,沒有。一個static字段在整個AppDomain之間共享,而不是每個線程。

如果你想爲每個線程分開值,我建議使用ThreadLocal<T>來存儲後備數據,因爲這將爲每個線程數據提供一個很好的包裝。

此外,在C#中,它通常通過更好,而不是Lazy<T>經由雙重檢查鎖定實現懶單。這看起來像:

public sealed class CurrentSingleton // Seal your singletons if possible 
{ 
    private static Lazy<CurrentSingleton> uniqueInstance = new Lazy<CurrentSingleton>(() => new CurrentSingleton()); 
    private CurrentSingleton() { } 

    public static CurrentSingleton Instance // use a property, since this is C#... 
    { 
     get { return uniqueInstance.Value; } 
    } 
} 

若要使類,提供每個線程一個實例,你可以使用:

public sealed class InstancePerThread 
{ 
    private static ThreadLocal<InstancePerThread> instances = new ThreadLocal<InstancePerThread>(() => new InstancePerThread()); 

    private InstancePerThread() {} 
    public static InstancePerThread Instance 
    { 
     get { return instances.Value; } 
    } 
} 
+0

我不知道'ThreadLocal '。使用''ThreadStatic''可能會更好。 –

+0

@TimothyShields是的,這是一個更好的抽象。你可以自己清理,初始化要好得多。 –

+0

@Reed Copsey使用你的類我有currentCounter = 13或currentCounter = 18。所以在某些情況下,我們仍然有相同的情況......這不好,不是嗎? – makcis

0

使用鎖定對象的可確保只有一個值被創建;你可以通過在你的CurrentSingleton構造函數中加入一些日誌來驗證這一點。

不過,我覺得有一個小的差距在你的邏輯:假設兩個線程同時調用此方法,而uniqueInstance爲空。兩者都將評估= null子句,並進入鎖定狀態。一個會贏,鎖定syncRoot,初始化爲uniqueInstance。當lock塊結束時,另一個將獲得自己的鎖,並再次初始化uniqueInstance

你需要測試甚至是否uniqueInstance爲空之前syncRoot鎖定。

+0

在鎖內部有一個單獨的檢查,因此實際上可以在.NET內存模型(微軟CLR的實現)中工作。當然,理論上需要記憶障礙是正確的,但雙重檢查鎖定「大部分」沒問題。 –

0

不管你做什麼,你都永遠不會得到currentCounter = 0。 因爲我們忘記了應用程序/ C#代碼也在某個線程中運行,並且有一些由C#設置的優先級來運行代碼。如果您通過在Main方法和CurrentSingleton中放置斷點來調試代碼,您會注意到這一點。到達併爲CurrentSingleton創建新對象時,for循環可能會迭代3或4或任何數字。迭代速度很快,代碼比較空值和Object或Object和空值。我認爲這是一個抓住。

Reed得到了一點靜態總是會因此共享你需要更改下列方式

public class CurrentSingleton 
    { 
    [ThreadStatic] 
    private static CurrentSingleton uniqueInstance = null; 
    private static object syncRoot = new Object(); 
    private CurrentSingleton() { } 

    public static CurrentSingleton getInstance() 
    { 
     if (uniqueInstance == null) 
      uniqueInstance = new CurrentSingleton(); 

     return uniqueInstance; 
    } 
} 

你的代碼,並按照分析你在第70次迭代得到兩個不同的對象,但是,那是後話只是不匹配可能爲null,Object或Object爲null。爲了獲得成功,你需要使用兩個不同的對象[ThreadStatic]

+0

使用'[ThreadStatic]'時不需要鎖定,因爲沒有任何可能的方式讓2個線程在資源上發生資源爭用...... –

+0

我更正了代碼。我只是在調試時發現我的分析。如果我錯了,請糾正我。 – Dnyanesh

+0

您只需要1個空檢查 - 不是其中的2個。 –