2012-07-26 25 views
10

tengo una clase Singleton similar a este C# Singleton seguro para subprocesos

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? 
     } 
    } 
} 

El método CallMe

será llamado por servicio web:

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

Las preguntas: 1) es el m_cacheObjects es hilo ¿seguro? ¿Qué ocurre si se cambia m_cacheObjects (debido al temporizador) durante la interacción del bucle (en CallMe())?

2) ¿Se creará un nuevo subproceso cuando se llame al servicio web CallMeWebService()?

+1

1) a) No, 1) b) No puede estar seguro, 2) Sí – Jodrell

+0

Seguiré las respuestas a continuación para hacer que la inicialización estática sea segura para subprocesos. – user1553857

+1

Ahora me preocupa más la lista estática m_cacheObjects. Pausa implícitamente (modo de depuración) en el ciclo foreach y espero a que el temporizador marque y cambie la lista m_cacheObjects, suponiendo que el ciclo foreach fallará debido a la modificación de la lista, pero continuará repitiendo sin excepción. ¿Esto es normal? – user1553857

Respuesta

4

Este es un muy buen recurso sobre cómo implementar el patrón singleton de una manera segura hilo: 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; 
     } 
    } 
} 

Esto simplemente se asegura de que nunca habrá más de una instancia. También necesita aplicar el bloqueo para su método personalizado.

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

@Downvoter No me importa un voto negativo si me lo merezco, pero, por favor, ¿podrías poner un comentario al respecto? Como es simplemente parece que uno de los otros que respondieron quería presionarme en la lista de respuestas independientemente de lo que dije. – Mithon

8

1: No, una lista estática no es automáticamente segura para subprocesos; debe proteger m_cacheObjects manualmente

2: Es un detalle de implementación; a primera vista, parece que se expone a sí mismo como un método de sincronización, pero cómo lo hace por completo

En realidad, su inicialización estática tampoco es segura para subprocesos; Podría usar fuerza bruta en un escenario donde se usaron dos instancias diferentes de Singleton. Tomaría repetición producirlo, pero sucedería.

Francamente, a menos que tenga una muy buena razón para no hacerlo, el patrón Singleton más simple pero más seguro es simplemente:

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

Quizás 'Lazy <>'? –

+0

@LB note que mencioné "más simple". Para ser sincero, las situaciones que necesitan un singleton perezosamente inicializado son muy raras; a: los singletons deberían ser raro, b: yo Necesitarías saber que 'Singleton' era caro de crear, yc: necesitarías saber que hay cosas útiles que se pueden hacer * sin * necesitarlo. En este caso, creo que en realidad falla los 3 criterios. –

+0

De forma manual, ¿quiere decir usar la palabra clave de bloqueo u otra cosa? –

0

No es seguro para subprocesos.

creo que usaría una cerraduratanto en 'MyTimerCallBack' y los métodos 'CallMe'

0

1) No, m_cacheObjects no es seguro para subprocesos.

2) Sí, se creará un nuevo subproceso (bueno, puede que no sea un subproceso nuevo, sino un subproceso tomado del grupo de subprocesos).

Deberá proteger m_cacheObjects con la declaración lock.Además, en el método CallMe Me gustaría recomendar para crear una copia local de m_cacheObjects: Método

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

Nueva CallMe:

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

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

y actualizada MyTimerCallBack método:

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

Y por favor también implemente Singleton seguro para subprocesos (lea otras respuestas para más detalles).

+0

entiendo el nuevo método CallMe. pero no estoy seguro acerca de MyTimerCallBack, si estoy seguro de que m_cacheObjects se actualizará antes del siguiente tic del temporizador, ¿todavía necesito bloquearlo? – user1553857

+0

Una sola instrucción 'lock' no tiene sentido. Al usar 'lock' en' MyTimerCallback' puede estar seguro de que 'CallMe' esperará hasta que MyTimerCallback finalice. Y viceversa: el bloqueo de 'CallMe' asegura que' m_cacheObjects' no se actualizará hasta que se copie a 'localCopy'. – Alexander

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; 
    } 
} 

Explicar:

[MethodImpl (MethodImplOptions.Synchronized)] Esto le dirá al compilador que el acceso a la "Instancia" está "sincronizado" por lo que el sistema se encargue de las llamadas a este parámetro.

Esto es Thread-Safe.

EDIT: (También, el "bloqueo()" ejemplos no son seguros Coz, u puede desactivar la seguridad hilo con "Monitor.Exit (Singleton);"!)

Cuestiones relacionadas