2010-05-16 19 views
12

Hace poco leí sobre las barreras de memoria y el problema de la reordenación y ahora tengo algo de confusión al respecto.Barrier de memoria por instrucción de bloqueo

cuenta la situación siguiente:

private object _object1 = null;  
private object _object2 = null; 
private bool _usingObject1 = false; 

private object MyObject 
{ 
    get 
    { 
     if (_usingObject1) 
     { 
      return _object1; 
     } 
     else 
     { 
      return _object2; 
     } 
    } 
    set 
    { 
     if (_usingObject1) 
     { 
      _object1 = value; 
     } 
     else 
     { 
      _object2 = value; 
     } 
    } 
} 

private void Update() 
{ 
    _usingMethod1 = true; 
    SomeProperty = FooMethod(); 
    //.. 
    _usingMethod1 = false; 
} 
  1. En Update método; ¿La instrucción _usingMethod1 = true se ejecuta siempre antes de obtener o configurar la propiedad? o debido a un problema de reordenamiento, no podemos garantizar eso?

  2. debemos utilizar volatile como

    private volatile bool _usingMethod1 = false; 
    
  3. Si utilizamos lock; podemos garantizar entonces cada declaración dentro de la cerradura se ejecutará en orden como:

    private void FooMethod() 
    { 
        object locker = new object(); 
        lock (locker) 
        { 
         x = 1; 
         y = a; 
         i++; 
        } 
    } 
    

Respuesta

28

El tema de las barreras de memoria es bastante complejo. Incluso hace tropezar a los expertos de vez en cuando. Cuando hablamos de una barrera de memoria realmente estamos combinando dos ideas diferentes.

  • valla Acquire: Una barrera de memoria en la que otro lee & escrituras no se les permite moverse antes la valla.
  • Valla de liberación: una barrera de memoria en la que otras lecturas & no se pueden mover después de la guía.

Una barrera de memoria que crea sólo una de dos veces se llama un medio-cerca. Una barrera de memoria que crea ambos se denomina a veces de valla completa.

La palabra clave volatile crea mitades. Las lecturas de campos volátiles tienen semántica de adquisición, mientras que las escrituras tienen semántica de lanzamiento. Eso significa que no se puede mover ninguna instrucción antes de una lectura o después de una escritura.

La palabra clave lock crea vallas completas en ambos límites (entrada y salida). Eso significa que no se puede mover ninguna instrucción antes o después de cada límite.

Sin embargo, todo esto es discutible si solo nos preocupa un hilo. Ordenar, como se percibe por ese hilo, siempre se conserva.De hecho, sin esa garantía fundamental, ningún programa funcionaría correctamente. El verdadero problema es cómo otros subprocesos perciben lecturas y escrituras. Ahí es donde debes preocuparte.

Así que para responder a sus preguntas:

  1. Desde la perspectiva de un solo hilo ... sí. Desde la perspectiva de otro hilo ... no.

  2. Depende. Eso podría funcionar, pero necesito tener una mejor comprensión de lo que estás tratando de lograr.

  3. Desde la perspectiva de otro hilo ... no. Las lecturas y escrituras son libres de moverse dentro de los límites de la cerradura. Simplemente no pueden moverse fuera de esos límites. Es por eso que es importante que otros hilos también creen barreras de memoria.

+0

Gracias por la información, realmente me ayuda a entender el concepto más ... Lo que necesito lograr es asegurarme de que la instrucción "_usingMethod1 = true" siempre se realice antes de la siguiente instrucción SomeProperty = FooMethod(); En el senario multithread ¿cómo lograr eso? es por: _usingMethod1 = true; Thread.MemoryBarrier(); SomeProperty = FooMethod(); o bloqueo para vallas completas, por lo que no se debe volver a marcar: bloqueo (casillero) {_usingMethod1 = true; } SomeProperty = FooMethod(); o tal vez simplemente haciendo que _usingMethod1 sea una variable volátil. Gracias por tu ayuda. –

+2

Me gustaría envolver todo el contenido del método de actualización en un bloqueo. Además de las barreras de memoria, también garantiza la atomicidad, que es igualmente importante. Además, estos modismos sin bloqueo (a través de volátiles, Thread.MemoryBarrier, etc.) son increíblemente difíciles de corregir. –

4

El volátiles palabra clave no logra nada aquí. Tiene garantías muy débiles, no implica una barrera de memoria. Su código no muestra la creación de otro subproceso, por lo que es difícil adivinar si se requiere bloqueo. Sin embargo, es un requisito difícil si dos hilos pueden ejecutar Update() al mismo tiempo y usar el mismo objeto.

Tenga en cuenta que su código de bloqueo como se publicó no bloquea nada. Cada hilo tendría su propia instancia del objeto "Locker". Tienes que convertirlo en un campo privado de tu clase, creado por el constructor o un inicializador. Por lo tanto:

private object locker = new object(); 

private void Update() 
{ 
    lock (locker) 
    { 
     _usingMethod1 = true; 
     SomeProperty = FooMethod(); 
     //.. 
     _usingMethod1 = false; 
    } 
} 

Tenga en cuenta que también habrá una carrera en la asignación SomeProperty.

+0

El volátil tiene una barrera de memoria; y entonces le pregunté si esa barrera de memoria suprimiría la reorientación, así que el _usingMethod1 = true siempre se garantizaría para ejecutar antes de obtener o establecer la propiedad SomeProperty, Proporciono un bloqueo solo para la barrera de memoria, no para el problema de sincronización con otros hilos y así Lo hago una variable local dentro del método en propósito porque estaba preguntando si evitará volver a ordenar las instrucciones dentro de la cerradura. –

+1

Un único hilo * siempre * tiene una vista consistente de las variables que utiliza. Los programas no podrían funcionar si ese no fuera el caso. –

Cuestiones relacionadas