2011-05-28 7 views
13

La documentación para LazyThreadSafetyMode estados que el uso de la ExecutionAndPublication valor podría causar bloqueos si el método de inicialización (o el constructor por defecto, si no hay un método de inicialización) utiliza bloqueos internos. Estoy tratando de comprender mejor los ejemplos que podrían causar un punto muerto al usar este valor. En mi uso de este valor, estoy inicializando un ChannelFactory. No puedo ver el constructor de ChannelFactory usando bloqueos internos (revisando la clase con Reflector), así que creo que este escenario no se ajusta a la posible situación de interbloqueo, pero tengo curiosidad por saber qué situaciones podrían causar un punto muerto y si podría haber un posible deadlock inicializando ChannelFactory.Lazy <T> ExecutionAndPublication - ejemplos que podrían causar interbloqueo

tanto, para resumir, mis preguntas son:

  1. ¿Es posible hacer que un punto muerto inicializar el ChannelFactory usando ExecutionAndPublication?

  2. ¿Cuáles son algunas formas posibles de causar un interbloqueo inicializando otros objetos utilizando ExecutionAndPublication?

Supongamos que tenemos el siguiente código:

class x 
{ 
    static Lazy<ChannelFactory<ISomeChannel>> lcf = 
     new Lazy<ChannelFactory<ISomeChannel>>(
     () => new ChannelFactory<ISomeChannel>("someEndPointConfig"), 
     LazyThreadSafetyMode.ExecutionAndPublication 
     ); 

    public static ISomeChannel Create() 
    { 
     return lcf.Value.CreateChannel(); 
    } 
} 

Respuesta

9
  1. Es como documentada - si no utiliza ningún bloqueo, este uso puede no causar bloqueos.
  2. Imagine que tiene un valor diferido que inicializa leyendo de una base de datos, pero desea asegurarse de que solo un hilo está accediendo al DB en cualquier momento. Si tiene otro código que accede a la base de datos, puede tener un punto muerto. Considere el siguiente código:
void Main() 
{ 
    Task otherThread = Task.Factory.StartNew(() => UpdateDb(43)); 
    Thread.Sleep(100); 
    Console.WriteLine(lazyInt.Value); 
} 

static object l = new object(); 
Lazy<int> lazyInt = new Lazy<int>(Init, LazyThreadSafetyMode.ExecutionAndPublication); 

static int Init() 
{ 
    lock(l) 
    { 
     return ReadFromDb(); 
    } 
} 

void UpdateDb(int newValue) 
{ 
    lock(l) 
    { 
     // to make sure deadlock occurs every time 
     Thread.Sleep(1000); 

     if (newValue != lazyInt.Value) 
     { 
      // some code that requires the lock 
     } 
    } 
} 

Init() lee de la base de datos, por lo que tiene que utilizar el bloqueo. UpdateDb() escribe en la base de datos, por lo que también necesita el bloqueo, y dado que Lazy utiliza un bloqueo interno también en este caso, provoca un interbloqueo.

En este caso, sería fácil de solucionar el punto muerto moviendo el acceso a lazyInt.Value en UpdateDb() fuera del estado de bloqueo, pero puede que no sea tan trivial (o evidente) en otros casos.

+1

Excelente respuesta @svick - ejemplo clásico de bloqueos anidados adquiridos en el orden opuesto, esto está en la línea de lo que estaba pensando - gran ejemplo para aclarar el escenario, ¡gracias! – dugas