С# Singleton Thread safe
У меня есть одноэлементный класс, похожий на этот
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 будет вызываться веб-службой:
[WebMethod]
public void CallMeWebService()
{
Singleton.Instance.CallMe();
}
Вопросы:
1) Является ли m_cacheObjects потокобезопасным? что произойдет, если m_cacheObjects будет изменен (из-за таймера) во время интервала цикла (в CallMe())?
2) Будет ли новый поток создаваться при вызове Webservice CallMeWebService()?
Ответы
Ответ 1
1: Нет, статический список не является автоматически потокобезопасным; вы должны защитить m_cacheObjects
вручную
2: Это деталь реализации; на первый взгляд похоже, что он раскрывает себя как метод синхронизации, но как он это делает, полностью зависит от него.
Фактически, ваша статическая инициализация также не является потокобезопасной; Я мог бы скорректировать сценарий, в котором использовались два разных экземпляра Singleton
. Для его выполнения потребуется повторение, но это произойдет.
Честно говоря, если у вас нет по-настоящему веской причины, простейший, но безопасный одноэлементный шаблон просто:
private static readonly Singleton m_instance = new Singleton();
Ответ 2
Я бы посоветовал вам написать статью о том, как создавать потокобезопасные синглтоны в http://csharpindepth.com/Articles/General/Singleton.aspx
Ответ 3
//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)] Это сообщит компилятору, что доступ к "Экземпляру" "Синхронизирован", поэтому система заботится о вызовах этого параметра.
Это потокобезопасно.
EDIT:
(Кроме того, примеры "Lock()" небезопасны! Coz, u может отключить поток Безопасность с помощью "Monitor.Exit(Singleton);" )
Ответ 4
Это довольно хороший ресурс о том, как реализовать одноэлементный шаблон в потоковом режиме: 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
}
}
}
Ответ 5
Он не является потокобезопасным.
Я полагаю, что использовал бы блокировку в методах "MyTimerCallBack" и "CallMe"
Ответ 6
1) Нет, m_cacheObjects
не является потокобезопасным.
2) Да, будет создан новый поток (ну, может быть, это не новый поток, а поток, связанный с пулом потоков).
Вам нужно защитить m_cacheObjects
оператором lock
. Кроме того, в методе 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 (читайте другие ответы для деталей).