2012-02-23 14 views
7

me encontré con algo de código Singleton hoy en nuestra base de código y no estaba seguro de si el siguiente era seguro para subprocesos:atomicidad de C# coalescente operador

public static IContentStructure Sentence{ 
    get { 
     return _sentence ?? (_sentence = new Sentence()); 
    } 
} 

Esta afirmación es equivalente a:

if (_sentence != null) { 
     return _sentence; 
} 
else { 
    return (_sentence = new Sentence()); 
} 

Creo que ?? es solo un truco de compilación y que el código resultante aún NO es atómico. En otras palabras, dos o más hilos podrían encontrar que _sentence sea nulo antes de establecer _sentencia en una nueva Sentencia y devolverla.

Para garantizar la atomicidad, tendríamos que cerrar ese trozo de código:

public static IContentStructure Sentence{ 
    get { 

     lock (_sentence) { return _sentence ?? (_sentence = new Sentence()); } 
    } 
} 

Es eso todo correcto?

+2

http://csharpindepth.com/Articles/General/Singleton.aspx – SLaks

+2

Excepto que no se puede bloquear en algo que es nula, por lo que su solución no va a funcionar. – vcsjones

+0

buen punto. Sí, tendrías que crear otro objeto para cerrar. Buena atrapada. – Adam

Respuesta

11

Tiene razón; no es para nada seguro para subprocesos.

1

Puede usar Interlocked.CompareExchange con null para obtener una operación ?? -esque atómica.

// I made up my own Sentence type 
Sentence current = null; 
var whenNull = new Sentence() {Text = "Hello World!"}; 

var original = Interlocked.CompareExchange(ref current, new Sentence() { Text = "Hello World!" }, null); 

Assert.AreEqual(whenNull.Text, current.Text); 
Assert.IsNull(orig); 

// try that it won't override when not null 
current.Text += "!"; 
orig = Interlocked.CompareExchange(ref current, new Sentence() { Text = "Hello World!" }, null); 

Assert.AreEqual("Hello World!!", current.Text); 
Assert.IsNotNull(orig); 
+0

La desventaja de eso es que creas una nueva 'Oración' para cada pase, ¿no? –

+2

@DrewNoakes: Estás en lo cierto. Es más idiomático hacer el intercambio de comparación solo si la corriente es nula. Entonces, la única vez que crea el objeto dos veces es una carrera improbable. Si no es aceptable crear el objeto dos veces, existen otras técnicas que puede usar. –

+0

Entonces, debería poder hacer lo siguiente: var x = current ?? Interlocked.CompareExchange (ref current, new Sentence(), null) ?? corriente; –

15

me encontré con algo de código Singleton hoy en nuestra base de código

¿Tiene dicho código ofuscado a través de su base de código? Este código hace lo mismo:

if (_s == null) 
    _s = new S(); 
return _s; 

y es aproximadamente mil veces más fácil de leer.

Creo que ?? es solo un truco de compilación y que el código resultante aún NO es atómico

Estás en la correcta. C# hace las siguientes garantías de atomicidad:

Lee y escribe de los siguientes tipos de datos son atómica: bool, tipos char, byte, SByte, cortos, ushort, uint, int, float, y de referencia. Además, las lecturas y escrituras de tipos enum con un tipo subyacente en la lista anterior también son atómicas. No se garantiza que las lecturas y escrituras de otros tipos, incluidos long, ulong, double y decimal, así como los tipos definidos por el usuario, sean atómicas. Además de las funciones de la biblioteca diseñadas para tal fin, no existe garantía de lectura, modificación y escritura atómica, como en el caso de incremento o disminución.

El operador nulo coalescente no figura en esa lista de garantías.

Para garantizar la atomicidad, tendríamos que cerrar ese trozo de código:

lock (_sentence) { return _sentence ?? (_sentence = new Sentence()); } } }  

Santo cielo no. ¡Eso se bloquea inmediatamente!

Lo que se debe hacer es una de:

  • dejar de intentar escribir código multiproceso.
  • Escriba un singleton usando uno de los patrones de singleton seguros que Jon Skeet documenta en su página sobre singletons.
  • Usa la clase Lazy<T>.
  • Bloquea un objeto dedicado al bloqueo de esa variable.
  • Utilice un Intercambio de comparación entrelazado para realizar una prueba atómica y configurarlo.