2010-11-23 11 views
6

tengo algo como lo siguiente en C#:.NET resultado en caché flujos seguros

private double _x; 
private bool _xCalculated; 

private double GetX() { 
    if (!_xCalculated) { 
     _x = ... // some relatively expensive calculation 
     _xCalculated = true; 
    } 

    return _x; 
} 

Mi pregunta es, ¿es seguro para subprocesos? Por lo que puedo decir, el peor resultado de esto es que dos o más hilos entran en este método al mismo tiempo y calculan _x varias veces, pero se garantiza que el resultado será el mismo para cualquier instancia de esta clase, por lo que no es un problema particularmente grande.

¿Entiendo esto correcto?

Respuesta

6

Algunas observaciones:

  1. El almacén en el doble podría no ser atómica
  2. La escritura en el bool debe ser atómico
  3. Dependiendo de la arquitectura de la CPU, las operaciones de la memoria pueden reordenarse
  4. Si no se realiza ninguna reordenación, el código debería funcionar

Aunque creo que el x86 memory ordering guarantees hace esto seguro, no estoy del todo seguro de eso. Las garantías de orden de memoria de .net se han fortalecido recientemente (creo en .net 4) para que coincida con las garantías de x86.

Memory Model in .net
More on memory ordering
Esto indica que las tiendas no se reordenan en .NET que creo que significa que su código es seguro. Pero la programación sin bloqueos es difícil, por lo que podría pasar por alto algunos problemas sutiles. Quizás la lectura en la cláusula if puede causar problemas.

Recomiendo no usar este código a menos que sea un experto en subprocesos y realmente necesite el rendimiento. De lo contrario, use algo más explícito como bloqueos. Las cerraduras no son tan caras si no se disputan.

+1

De hecho. ¿Por qué especular sobre cómo podría funcionar el hardware X mientras que Y no, cuando un BLOQUEO es más simple, más claro, garantizado y la diferencia de rendimiento no vale la pena hablar? – smirkingman

+0

Haré pruebas de rendimiento para encontrar la mejor implementación para mis propósitos, pero quería saber si esta solución es válida para empezar. – derkyjadex

+0

El problema con su solución es que es tan difícil saber si es correcta. Y dado que los problemas de enhebrado no son deterministas, ni siquiera puedes simplemente probarlos. – CodesInChaos

2

No es seguro para subprocesos. Y sí, tu comprensión es correcta. Puede usar la declaración lock() para que sea seguro para subprocesos.

http://msdn.microsoft.com/en-us/library/c5kehkcz(VS.71).aspx

private object objLock = new object(); 
private double GetX() { 
    lock(objLock) { 
     if (!_xCalculated) { 
      _x = ... // some relatively expensive calculation 
      _xCalculated = true; 
     } 
    } 
    return _x; 
} 
+2

Por favor, evite el uso de bloqueo (esto). Es mejor bloquear un objeto privado lockObject = new object(); Ver http://www.toolazy.me.uk/template.php?content=lock(this)_causes_deadlocks.xml –

+0

Y afirmar que no es seguro es un reclamo audaz sin dar algunas razones para ese reclamo. – CodesInChaos

+0

ok, el bloqueo (esto) fue una instantánea. pero definitivamente no es seguro para subprocesos: _x = ... es al menos una operación. _xCalculated = true sigue como una segunda operación después de él, por lo que puede haber condiciones de carrera. – Hinek

1

Depende de la plataforma, no creo que esto es seguro con el modelo de memoria de .NET de acuerdo con sus especificaciones, pero creo que está bien en el actual Microsoft CLR. El problema es la extensión en que una CPU tiene permiso para escribir en la memoria de reorden.

Por favor alguien puede subir con los enlaces detallados a la especificación ...