C#Singleton线程安全

我有一个类似于此的单例类

public class Singleton { private static Singleton m_instance; private Timer m_timer; private static List m_cacheObjects; private Singleton() { m_cacheObjects = new List(); 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服务调用:

  [WebMethod] public void CallMeWebService() { Singleton.Instance.CallMe(); } 

问题:1)m_cacheObjects是否是线程安全的? 如果在循环交互期间(在CallMe()中)m_cacheObjects被更改(因为计时器)会发生什么?

2)在调用Webservice CallMeWebService()时是否会创建新线程?

1:不,静态列表不是自动线程安全的; 您必须手动保护m_cacheObjects

2:这是一个实现细节; 乍一看,它似乎将自己暴露为同步方法,但它如何做到完全取决于它

实际上,您的静态初始化也不是线程安全的; 我可以强制使用两个不同的Singleton实例的场景。 生产它需要重复,但它会发生。

坦率地说,除非你有充分的理由不这样做,否则最简单但最安全的单身人士模式就是:

 private static readonly Singleton m_instance = new Singleton(); 

我建议你有关于如何在http://csharpindepth.com/Articles/General/Singleton.aspx上创建线程安全单例的文章。

 //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)]这将告诉编译器对“Instance”的访问是“已同步”,因此系统会关注对此Parameter的调用。

这是线程安全的。

编辑:(另外,“Lock()”示例不安全!因为,你可以通过“Monitor.Exit(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 } } } 

它不是线程安全的。

我相信我会在“MyTimerCallBack”和“CallMe”方法中使用

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

2)是的,将创建新线程(好吧,它可能不是新线程,而是从线程池中删除线程)。

您需要使用lock语句保护m_cacheObjects 。 另外,在CallMe方法中,我建议创建m_cacheObjects的本地副本:

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

新的CallMe方法:

 List localCopy; lock (syncRoot) { localCopy = new List(m_cacheObjects); } foreach (var nextObject in localCopy) { // Do some work } 

并更新了MyTimerCallBack方法:

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

并且还请实现线程安全的Singleton(有关详细信息,请阅读其他答案)。