En un programa multiproceso (Java o .Net), ¿puedo suponer que copiar una variable es atómico?

StackOverflow https://stackoverflow.com/questions/473111

Pregunta

Estaba preocupado por las condiciones de carrera en una aplicación que estoy diseñando, cuando me preguntaba sobre esta pregunta.

Digamos que tengo una gran variedad o colección de algún tipo que es administrada por un componente de mi programa, llamemos a ese componente Monitor. Su trabajo es verificar regularmente si la colección está `` sucia '', i. mi. ha cambiado recientemente, y si es así, escriba una instantánea en el disco (esto es para verificar la aplicación en caso de que ocurra un bloqueo) y márquelo como limpio nuevamente.

Otros componentes del mismo programa, que se ejecutan en un hilo diferente, llaman a los métodos del Monitor para agregar o modificar datos en la matriz / colección. Esos métodos marcan la colección como sucia.

Ahora, los métodos de alteración se ejecutan en los hilos de los otros componentes, ¿verdad? Y si no tengo tanta suerte, podrían llamarse mientras la instantánea se escribe en el disco, cambiar los datos que ya se han escrito, establecer el indicador sucio y el subproceso del monitor lo desarma justo después de eso, sin haber guardado los cambios ( ya había pasado el elemento cuando cambió). Así que tengo una colección sucia que está marcada como limpia.

Por un tiempo, pensé que tal vez podría resolver el problema haciendo una copia temporal de la colección, márquela limpia y luego vaya y serialice la copia. Pero la copia sería atómica, i. mi. ¿Puedo estar seguro de que la colección no cambiará mientras la copio?

Mientras tanto, creo que he encontrado mejores soluciones, como

  • establecer un indicador de bloqueo antes de comenzar a escribir en el disco y hacer que los métodos de modificación de datos esperen hasta que el indicador se desactive
  • haga que los métodos de alteración de datos escriban en una " cola de cambios " en lugar de ir directamente a la colección y tener el hilo que hace el proceso de escritura del disco que hace cola

y creo que la bandera de bloqueo podría ser la mejor opción. Pero todavía tengo curiosidad: ¿copiar una variable atómica?


Seguimiento : Tal vez esto debería ir en una pregunta propia, pero en realidad es muy parecido. De acuerdo con las respuestas a continuación, mi '' bandera de bloqueo '' enfoque también podría no funcionar, ¿verdad? Debido a que el método de alteración de datos puede verificar el indicador de bloqueo mientras se establece en "bloqueado" valor y decida que no está bloqueado. Entonces, necesito una construcción especial como un mutex si realmente quiero hacerlo bien, ¿correcto?


Felicitaciones a erickson por su respuesta muy útil en mi seguimiento. Realmente debería haber hecho estas dos preguntas para haber aceptado dos respuestas. Por favor, vote también por él.

¿Fue útil?

Solución

No. Por ejemplo, las variables largas en Java no son atómicas en máquinas de 32 bits.

Además, hay un "almacenamiento en caché de hilos" problema: a menos que su variable sea volátil o esté dentro del bloque sincronizado, es posible que otro subproceso no vea el cambio en el valor de la variable. Esto es cierto para todo tipo de variables, no solo largas.

Lea aquí: http://gee.cs.oswego.edu/dl /cpj/jmm.html , especialmente "atomicidad" y "visibilidad" párrafos.

Otros consejos

No, no es atómico. Consulte esta pregunta para saber por qué y qué hacer al respecto.

Eche un vistazo a java.util.concurrent.atomic : puede haber algunas cosas buenas que puede usar.

Debe preocuparse por la visibilidad de los cambios en otros hilos cuando trabaje en la JVM. En general, debe realizar sus asignaciones dentro de un bloque sincronizado , o la variable debe ser volátil , o debe usar un envoltorio de variable del java.util. paquete concurrent.atomic .

Sin embargo, en su caso, parece que solo tiene un hilo que alguna vez borra el '' sucio '' flag & # 8212; el hilo que persiste los datos. Si ese es el caso, borre la bandera antes de escribir los datos. Si otros hilos lo configuran mientras está escribiendo los datos, permanecerá configurado hasta la próxima escritura programada. Usaría un AtomicBoolean , para darle atomicidad a su hilo de persistencia entre verificar la bandera y borrarla, así:

private final AtomicBoolean dirty = new AtomicBoolean();

/**
 * Any method that modifies the data structure should set the dirty flag.
 */
public void modify() {
  /* Modify the data first. */
  ...
  /* Set the flag afterward. */
  dirty.set(true);
}

private class Persister extends Thread {
  public void run() {
    while (!Thread.interrupted()) {
      if (dirty.getAndSet(false)) {
        /* The dirty flag was set; this thread cleared it 
         * and should now persist the data. */
         ...
      }
    }
  }
}

Establecer un bit de 32 bits (al menos en .NET) es atómico, pero no sirve de nada. Debe leerlo para saber si está bloqueado, por lo que puede leerlo, y después de la lectura, otra persona lo lee antes de que pueda configurarlo, por lo que dos hilos terminan dentro el " protegido " código. Esto es exactamente para lo que son los objetos de sincronización reales (como la clase .NET Monitor). También puede usar un Interbloqueado para verificar e incrementar la variable de bloqueo.

Consulte también: Está accediendo a una variable en C # un atómico operación?

Depende mucho del hardware y JVM que esté ejecutando

En algunos hardware y algunas JVM, algunas copias serán atómicas pero es más seguro asumir que este no es el caso, incluso una simple asignación de entero a entero puede traducirse en cuatro instrucciones de máquina en hardware x856.

Las copias de cadenas y matrices pueden involucrar una secuencia de miles de instrucciones y es posible que dos subprocesos se actualicen simultáneamente.

Según tengo entendido, no siempre.

Int32 será, un Int64 no estará en un sistema de 32 bits ya que necesita 2 x 32 bits. Por lo tanto, no cabe en una celda de 32 bits.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top