2008-12-08 8 views
8

En un programa multiproceso que se ejecuta en una máquina de varias CPU, necesito acceder al estado compartido (_data en el código de ejemplo a continuación) usando lecturas/escrituras volátiles para asegurar la corrección.Uso de volátiles (Thread.VolatileRead/Thread.VolatileWrite) en C#

En otras palabras, ¿se pueden almacenar en memoria caché los objetos en la CPU?

Utilizando el ejemplo siguiente y suponiendo que los subprocesos múltiples accederán a los métodos GetValue y Add, necesito que ThreadA pueda agregar datos (usando el método Add) y ThreadB para poder ver/obtener esos datos agregados inmediatamente (usando el método GetValue). Entonces, ¿necesito agregar lecturas/escrituras volátiles a _data para asegurar esto? Básicamente, no quiero que los datos agregados se almacenen en caché en la CPU de ThreadA.

/No estoy Bloqueando (imponiendo acceso exclusivo a subprocesos) ya que el código debe ser ultrarrápido y no estoy eliminando ningún dato de _data, así que no es necesario bloquear _data.

Gracias.

**** Actualización ****************************

Obviamente piensan ustedes va usando bloqueo libre este ejemplo es mala idea. Pero, ¿qué efectos secundarios o excepciones podría enfrentar aquí?

¿Podría el diccionario escribir una excepción si 1 hilo está iterando los valores de lectura y otro hilo está iterando los valores para la actualización? ¿O simplemente experimentaría "lecturas sucias" (que estaría bien en mi caso)?

**** End Actualizar ****************************

public sealed class Data 
{ 
    private volatile readonly Dictionary<string, double> _data = new Dictionary<string, double>(); 

    public double GetVaule(string key) 
    { 
     double value; 
     if (!_data.TryGetValue(key, out value)) 
     { 
      throw new ArgumentException(string.Format("Key {0} does not exist.", key)); 
     } 
     return value; 
    } 

    public void Add(string key, double value) 
    { 
     _data.Add(key, value); 
    } 

    public void Clear() 
    { 
     _data.Clear(); 
    } 
} 

Gracias por las respuestas Con respecto a las cerraduras, los métodos son llamados casi constantemente por varios hilos, por lo que mi problema es con los bloqueos disputados, no con la operación de bloqueo real.

Así que mi pregunta es sobre el almacenamiento en caché de la CPU, ¿pueden almacenarse en caché los objetos (el campo de la instancia _data) en una CPU? ¿Necesito acceder al campo _data usando lecturas/escrituras volátiles?

/Además, estoy atrapado con .Net 2.0.

Gracias por su ayuda.

Respuesta

2

No creo que la volatilidad pueda ser un reemplazo del bloqueo si comienzas a llamar métodos sobre ella. Está garantizando que el hilo A y el hilo B ven la misma copia del diccionario, pero aún puede acceder al diccionario simultáneamente. Puede usar bloqueos de varios hilos para aumentar la concurrencia. Ver ReaderWriterLockSlim por ejemplo.

representa un bloqueo que se utiliza para gestionar el acceso a un recurso, lo que permite múltiples hilos para leer o acceso exclusivo para la escritura.

16

El MSDN docs para Dictionary<TKey, TValue> dicen que es seguro para los múltiples lectores pero no dan el "un escritor, varios lectores de" garantía de que algunas otras clases lo hacen. En resumen, no haría esto.

Usted dice que está evitando el bloqueo porque necesita que el código sea "ultrarápido" - ¿ha intentado bloquear para ver cuál es la sobrecarga? Los bloqueos no impugnados son muy baratos, y cuando se disputa la cerradura es cuando se beneficia de la seguridad adicional. Ciertamente, lo describiría exhaustivamente antes de decidirme a preocuparme por los problemas de concurrencia de una solución sin bloqueo. ReaderWriterLockSlim puede ser útil si realmente tiene varios lectores, pero parece que tiene un solo lector y un solo escritor, al menos en este momento; el bloqueo simple será más fácil en este caso.

3

El problema es que su método add:

public void Add(string key, double value) 
{ 
    _data.Add(key, value); 
} 

podrían causar que _data decida completamente re-organizar los datos que sostiene - en ese momento una petición GetVaule podría fallar en cualquier forma posible.

Necesita un bloqueo o una implementación de estructura de datos/estructura de datos diferente.

6

Creo que puede estar malinterpretando el uso de la palabra clave volatile (ya sea eso o lo soy, y alguien por favor no dude en corregirme). La palabra clave volatile garantiza que obtener y establecer operaciones en el valor de la variable en sí misma desde múltiples hilos siempre tratará con la misma copia. Por ejemplo, si tengo un bool que indica un estado, establecerlo en un hilo hará que el nuevo valor esté inmediatamente disponible para el otro.

Sin embargo, nunca cambia el valor de su variable (en este caso, una referencia). Todo lo que haces es manipular el área de memoria a la que apunta la referencia. Declararlo como volatile readonly (que, si mi comprensión es sólida, vence el propósito de volátil al nunca permitir que se establezca) no tendrá ningún efecto sobre los datos reales que se están manipulando (el almacén de fondo para el Dictionary<>).

Dicho todo esto, realmente necesita usar un candado en este caso. Su peligro se extiende más allá de la perspectiva de "lecturas sucias" (lo que significa que lo que leyó habría sido, en algún momento, válido) en territorio desconocido. Como dijo Jon, realmente necesitas una prueba de que el bloqueo produce un rendimiento inaceptable antes de tratar de seguir el camino de la codificación sin bloqueos. De lo contrario, ese es el epítome de la optimización prematura.

0

El volátil no se bloquea, no tiene nada que ver con la sincronización. En general, es seguro realizar lecturas sin bloqueo en datos de solo lectura. Tenga en cuenta que simplemente porque no elimina nada de _data, parece que llama a _data.Add(). Eso NO es de solo lectura. Entonces, sí, este código explotará en tu cara en una variedad de formas emocionantes y difíciles de predecir.

Use candados, es simple, es más seguro. Si eres un gurú sin candado (¡no lo eres!), Y el perfil muestra un cuello de botella relacionado con la contención del bloqueo, Y no puedes resolver los problemas de contención mediante particiones o cambiando a bloqueos por giro ENTONCES Y SOLO ENTONCES puedes investigue una solución para obtener lecturas sin bloqueos, lo que implicará escribir su propio diccionario desde cero y PUEDE ser más rápido que la solución de bloqueo.

¿Estás empezando a ver qué tan lejos de la base estás en tu forma de pensar aquí? ¡Solo usa un maldito candado!

1

La palabra clave volatile no se trata de bloquear, se usa para indicar que el valor del campo especificado puede cambiarse o leerse por un hilo diferente u otra cosa que se pueda ejecutar simultáneamente con su código. Esto es crucial para que lo sepa el compilador, ya que muchos procesos de optimización implican almacenar en caché el valor de la variable y reorganizar las instrucciones.La palabra clave volatile le dirá al compilador que sea "cauto" al optimizar las instrucciones que hacen referencia a la variable volatile.

Para el uso de múltiples hilos del diccionario, hay muchas formas de hacerlo. La forma más sencilla es utilizar la palabra clave lock, que tiene un rendimiento adecuado. Si necesita un mayor rendimiento, es posible que necesite implementar su propio diccionario para su tarea específica.

Cuestiones relacionadas