2012-07-26 69 views
10

我有類似這樣C#辛格爾頓線程安全

public class Singleton 
{ 
    private static Singleton m_instance; 
    private Timer m_timer; 
    private static List<CustomObject> m_cacheObjects; 

    private Singleton() 
    {  
     m_cacheObjects = new List<CustomObject>(); 
     m_timer= new Timer(MyTimerCallBack, 
          null, 
          TimeSpan.FromSeconds(60), 
          TimeSpan.FromSeconds(60));   
    } 

    public static Singleton Instance 
    { 
     get 
     { 
      if (m_instance == null) 
      { 
       m_instance = new Singleton(); 
      } 
      return m_instance; 
     } 
    } 

    private void MyTimerCallBack(object state) 
    { 
     //******** Update the list by interval here ****************** 

     m_cacheObjects = UpdateTheList(); 
    } 

    public void CallMe() 
    { 
     foreach (CustomObject obj in m_cacheObjects) 
     { 
      // do something here based on obj 

      // The question is, does the m_cacheObjects is thread safe?? 
      // what happen if the m_cacheObjects is changed 
      // during the loop interation? 
     } 
    } 
} 

的的CallMe方法的單身類將Web服務調用 1)是m_cacheObjects的線程安全?如果在循環交互期間(在CallMe()中)m_cacheObjects被更改(由於計時器)會發生什麼?

2)Webservice CallMeWebService()被調用時是否會創建一個新的線程?

+1

1)a)否,1 )b)您不能確定,2)是 – Jodrell 2012-07-26 08:18:57

+0

我將按照下面的回答使靜態初始化成爲線程安全的。 – user1553857 2012-07-26 08:47:47

+1

現在我更關注靜態列表m_cacheObjects。我在foreach循環中隱式暫停(調試模式),並等待計時器打勾並更改m_cacheObjects列表,假設foreach循環將由於列表修改而失敗,但它將繼續循環而無一例外。這是正常的嗎? – user1553857 2012-07-26 08:53:51

回答

4

這是如何實現在一個線程安全的方式Singleton模式一個很好的資源:http://msdn.microsoft.com/en-us/library/ff650316.aspx

public sealed class Singleton 
{ 
    private static volatile Singleton instance; 
    private static object syncRoot = new Object(); 

    private Singleton() {} 

    public static Singleton Instance 
    { 
     get 
     { 
    if (instance == null) 
    { 
     lock (syncRoot) 
     { 
      if (instance == null) 
      instance = new Singleton(); 
     } 
    } 

    return instance; 
     } 
    } 
} 

這只是可以確保絕不會有不止一個實例的更多。您還需要爲自定義方法應用鎖定。

public void CallMe() 
{ 
    lock (syncRoot) 
    { 
     foreach (CustomObject obj in m_cacheObjects) 
     { 
      // do something here based on obj 
     } 
    } 
} 
+4

@Downvoter如果我值得擁有它,我不介意downvote,但是你能否和它一起發表評論?因爲它看起來像其中一個答覆者想要推動我不管我說什麼都可以列出答案列表 – Mithon 2012-07-26 08:35:32

8

1:不,靜態列表不是自動線程安全的;你必須手動保護m_cacheObjects

2:這是一個實現細節;乍看起來它暴露了自己作爲一個同步的方法,但如何這是否是完全取決於它

其實,你的靜態初始化不是線程安全的兩種;我可以蠻力使用兩個不同的Singleton實例。它會重複生成它,但會發生。

坦率地說,除非你有一個很好的理由不,最簡單最安全的,但單例模式很簡單:

private static readonly Singleton m_instance = new Singleton(); 
+0

也許'懶惰<>'? – 2012-07-26 08:21:48

+0

@LB注意我提到「最簡單的」坦率地說,需要一個懶惰初始化單例的情況非常罕見; a:單例應該是很少,b:喲你需要知道'Singleton'的創建成本很高,而且c:你需要知道有沒有需要的東西可以做。在這種情況下,我認爲它實際上不符合所有3個標準。 – 2012-07-26 08:23:22

+0

通過手動,你的意思是使用鎖定關鍵字還是別的? – 2012-07-26 08:23:40

0

它不是線程安全的。

我相信我會在這兩個「MyTimerCallBack」使用和「的CallMe」方法

0

1)不,m_cacheObjects不是線程安全的。

2)是的,新的線程將被創建(好吧,它可能不是新的線程,而是線程池中的線程)。

您需要用lock聲明保護m_cacheObjects。此外,在方法的CallMe我建議創建的m_cacheObjects本地副本:

// create new field syncRoot 
private static readonly object syncRoot = new object(); 

新的CallMe方法:

List<CustomObject> localCopy; 
lock (syncRoot) 
{ 
    localCopy = new List<CustomObject>(m_cacheObjects); 
} 

foreach (var nextObject in localCopy) 
{ 
// Do some work 
} 

和更新的MyTimerCallBack方法:

lock (syncRoot) 
{ 
    m_cacheObjects = UpdateTheList(); 
} 

並請也實現線程安全的Singleton(閱讀其他答案的細節)。

+0

我瞭解新的CallMe方法。但不知道MyTimerCallBack,如果確定m_cacheObjects將在下一個計時器打勾前更新,我還需要鎖定它嗎? – user1553857 2012-07-26 08:57:25

+0

Single'lock'語句沒有意義。通過在MyTimerCallback中使用'lock',你可以確定'CallMe'將等到MyTimerCallback完成。反之亦然 - 鎖定'CallMe'確保'm_cacheObjects'不會被更新,直到它被複制到'localCopy'。 – Alexander 2012-07-26 09:20:24

5
//using System.Runtime.CompilerServices; 

private static volatile Singelton _instance; 

public static Singelton Instance 
{ 
    [MethodImpl(MethodImplOptions.Synchronized)] 
    get 
    { 
     if (_instance == null) 
     { 
      _instance = new Singelton(); 
     } 
     return _instance; 
    } 
} 

解釋:

[MethodImpl(MethodImplOptions.Synchronized)]這將告訴大家,進入「實例」是「同步」,這樣的系統大約需要調用該參數保健編譯器。

這是線程安全的。

編輯: (此外, 「鎖()」 的例子並不安全的堂妹,ü可以禁用與線程安全 「Monitor.Exit(辛格爾頓);」!)