2017-11-11 120 views
5

我想弄清楚單身模式設計。我想爲我的單例類中的每個線程創建分離的實例。所以我在下面提供了兩個設計。C#單線程模式設計ThreadStatic

它正在

class Program 
{ 
    static void Main(string[] args) 
    { 
     Task.Factory.StartNew(() => Console.WriteLine(SingletonClass.Instance.GetHashCode())); 
     Task.Factory.StartNew(() => Console.WriteLine(SingletonClass.Instance.GetHashCode())); 
     Console.ReadLine(); 
    } 
} 
public sealed class SingletonClass 
{ 
    [ThreadStatic] 
    private static SingletonClass _instance; 

    public static SingletonClass Instance 
    { 
     get 
     { 
      if (_instance == null) 
      { 
       _instance = new SingletonClass(); 
      } 
      return _instance; 
     } 
    } 
    private SingletonClass() 
    { 

    } 
} 

它不工作(投擲NullReferenceException異常並沒有被創建的實例。)

class Program 
{ 
    static void Main(string[] args) 
    { 
     Task.Factory.StartNew(() => Console.WriteLine(SingletonClass.Instance.GetHashCode())); 
     Task.Factory.StartNew(() => Console.WriteLine(SingletonClass.Instance.GetHashCode())); 
     Console.ReadLine(); 
    } 
} 
public sealed class SingletonClass 
{ 
    [ThreadStatic] 
    private static SingletonClass _instance = new SingletonClass(); 

    public static SingletonClass Instance 
    { 
     get 
     { 
      return _instance; 
     } 
    } 
    private SingletonClass() 
    { 

    } 
} 

我真的很奇怪,爲什麼不創建一個實例爲第二個設計。有人可以解釋一下嗎?

+0

C#編譯器生成一個靜態構造函數以實現該變量的初始化。 CLR很難保證它只運行一次。你需要'new'語句多次運行。 –

+0

我相信,即使這種方法奏效,或者你已經開始工作,這已經不再是單身。單線程應該是單線程而不是單線程。 – CodingYoshi

+0

'CodingYoshi'在單例描述中沒有任何地方表明它不能是每個線程。在其他語言前。 'D'對於每個線程來說它是完全正常的。因爲默認情況下,一切都是線程本地的。 – Bauss

回答

3

您的問題的答案主要與如何初始化類字段有關。

在第二個示例中,_instance字段在聲明處初始化。每次在聲明中初始化靜態字段時,都會創建靜態構造函數(如果您沒有聲明它)。在編譯時,初始化將被移入靜態構造函數。這意味着你最終將不得不像這樣(沒有複製的IL代碼,因爲它會更難理解):

public sealed class SingletonClass 
{ 
    [ThreadStatic] 
    private static SingletonClass _instance; 

    public static SingletonClass Instance 
    { 
     get 
     { 
      return _instance; 
     } 
    } 
    static SingletonClass() 
    { 
     _instance = new SingletonClass(); 
    } 
} 

的CLR保證了靜態構造函數被調用一次,不管如何你有很多線程。查看上面的代碼,這意味着對於您創建的兩個任務,_instance字段將僅初始化一次(因爲只有一個對靜態構造函數的調用)。

4

而不是使用[ThreadStatic]那麼你可以使用ThreadLocal<T>這將基本上實現你試圖用[ThreadStatic]

public sealed class SingletonClass 
{ 
    private static ThreadLocal<SingletonClass> _instance; 

    static SingletonClass() 
    { 
     _instance = new ThreadLocal<SingletonClass>(() => new SingletonClass()); 
    } 

    public static SingletonClass Instance 
    { 
     get 
     { 
      return _instance.Value; 
     } 
    } 

    private SingletonClass() 
    { 

    } 
} 

參見:https://msdn.microsoft.com/en-us/library/dd642243(v=vs.110).aspx瞭解更多信息。

編輯:回答你的問題。

在C#這樣做時:

private static SingletonClass _instance = new SingletonClass(); 

不管它是否標有[ThreadStatic]或者沒有,那麼它只會創建一個單一的靜態構造函數設置的SingletonClass實例。

C#沒有能力爲每個線程創建靜態構造函數。

這就是你可以使用ThreadLocal<T>。如果我們以您的代碼爲例,那麼SingletonClass的默認構造函數基本上變成了"thread-static"構造函數。

+2

這提供了一個替代方案,但不回答問題。 – CodingYoshi

+0

已更新我的回答 – Bauss

+0

謝謝,我認爲這是嵌套線程的一個很好的選擇。我將用ThreadLocal編輯我的項目代碼。但我仍想弄清楚我的問題。 – lucky