2010-12-14 12 views
6

¿Cuándo se deben usar los bloqueos? ¿Solo cuando se modifican datos o cuando se accede a ellos también?bloqueo solo cuando se modifica el método entero

public class Test { 
    static Dictionary<string, object> someList = new Dictionary<string, object>(); 

    static object syncLock = new object(); 

    public static object GetValue(string name) { 
     if (someList.ContainsKey(name)) { 
      return someList[name]; 
     } else { 
      lock(syncLock) { 
       object someValue = GetValueFromSomeWhere(name); 
       someList.Add(name, someValue); 
      } 
     } 
    } 
} 

¿Debería haber un bloqueo alrededor de todo el bloque o está bien simplemente agregarlo a la modificación real? Tengo entendido que aún podría haber alguna condición de carrera en la que una llamada no la haya encontrado y haya comenzado a agregarla, mientras que otra llamada podría haber pasado a la misma situación, pero no estoy seguro. Locking sigue siendo tan confuso. No me he encontrado ningún problema con el código anterior, pero podría ser afortunado hasta ahora. Cualquier ayuda anterior se apruebe, así como cualquier recurso útil sobre cómo/cuándo bloquear objetos.

Respuesta

8

Tiene que bloquear al leer también, o puede obtener datos no confiables, o incluso una excepción si una modificación simultánea cambia físicamente la estructura de datos de destino.

En el caso anterior, debe asegurarse de que varios subprocesos no intenten agregar el valor al mismo tiempo, por lo que necesita al menos un bloqueo de lectura mientras verifica si ya está presente. De lo contrario, varios subprocesos podrían decidir agregar, encontrar el valor no presente (ya que esta verificación no está bloqueada) y luego intentar agregar uno por vez (después de obtener el bloqueo)

Puede usar ReaderWriterLockSlim si tiene muchos lee y solo unas pocas escrituras. En el código anterior, adquirirás el bloqueo de lectura para hacer el control y actualizar a un bloqueo de escritura una vez que decidas que debes agregarlo. En la mayoría de los casos, solo se necesitaría un bloqueo de lectura (que permite que los hilos de lectura sigan ejecutándose en paralelo).

Hay un resumen de las primitivas de bloqueo .Net 4 disponibles here. Definitivamente deberías entender esto antes de adentrarte demasiado en el código multiproceso. Escoger el mecanismo de bloqueo correcto puede hacer una enorme diferencia de rendimiento.

Tiene toda la razón de que ha tenido suerte hasta el momento: esa es una característica frecuente de los errores de concurrencia. A menudo son difíciles de reproducir sin pruebas de carga específicas, lo que significa que el diseño correcto (y las pruebas exhaustivas, por supuesto) es vital para evitar averías y errores confusos en la producción.

+0

Gran enlace para todos los mecanismos de bloqueo disponibles. Solo había escuchado sobre 'lock'. – MyNameIsJob

+0

Buena suerte: si puedes dominar estas cosas, estarás en una pequeña minoría de desarrolladores que están bien preparados para un aumento en el desarrollo multinúcleo. –

1

Bloquea todo el bloque antes de verificar la existencia de name. De lo contrario, en teoría, otro hilo podría agregarlo entre el cheque y el código que lo agrega.

En realidad, bloquear cuando realizas el Add realmente no hace nada. Todo lo que haría es evitar que otro hilo agregue algo simultáneamente. Pero dado que ese otro hilo ya habría decidido que iba a hacer el complemento, simplemente trataría de hacerlo de todos modos tan pronto como se liberara el bloqueo.

+1

¿Otro proceso? La terminología es importante. –

+0

True enuf. Corregido –

1

Si solo se puede acceder a un recurso por varios hilos, no necesita ningún candado.

Si se puede acceder a un recurso por varios hilos y puede modificarse, entonces todos los accesos/modificaciones deben sincronizarse. En su ejemplo, si tarda mucho tiempo en regresar al GetValueFromSomeWhere, es posible hacer una segunda llamada con el mismo valor en name, pero el valor no se ha almacenado en el Dictionary.

0

ReaderWriterLock o la versión delgada si tiene menos de 4.0.

Adquirirá el bloqueo del lector para las lecturas (permitirá lecturas simultáneas) y actualizará el bloqueo al bloqueo del escritor cuando algo deba escribirse (solo permitirá una escritura en el momento y bloqueará todas las lecturas hasta que hecho, así como los hilos de escritura concurrentes).

Asegúrese de liberar los bloqueos con el patrón para evitar interbloqueos:

  void Write(object[] args) 
      { 


        this.ReaderWriterLock.AquireWriteLock(TimeOut.Infinite); 

        try 
        { 
         this.myData.Write(args); 

        } 
        catch(Exception ex) 
        { 

        } 
        finally 
        { 

         this.ReaderWriterLock.RelaseWriterLock(); 
        } 

      } 
Cuestiones relacionadas