2009-06-03 33 views
8

Todo el mundo sabe que esto no es hilo de seguridad:¿El C# '??' hilo de operador seguro?

public StringBuilder Builder 
{ 
    get 
    { 
     if (_builder != null) 
      _builder = new StringBuilder(); 
     return _builder; 
    } 
} 

¿Y esto?

public StringBuilder Builder 
{ 
    get { return _builder ?? (_builder = new StringBuilder()); } 
} 
+13

La especificación C# indica cuidadosamente qué operaciones son atómicas; los operadores coalescentes nulos no son atómicos. El operador nulo coalescente es solo un azúcar sintáctico para su primer fragmento de código. Pero tienes problemas más grandes aquí; a quién le importa si el campo es seguro para la rosca? ¡El constructor no es seguro para los hilos! –

+4

Para futuras preguntas en este sentido, sería útil si proporcionara una definición cuidadosamente redactada de exactamente qué significa "hilo seguro" para usted. La seguridad del subproceso no es absoluta; más bien, el código es seguro para subprocesos si el contrato de uso implementado por los llamantes es compatible con el esperado por el destinatario. Sin saber qué contrato espera, es imposible decir si el código lo sigue o no. –

Respuesta

10

COMENZAR EDITAR

Sobre la base de su título editado, el propio operador nulo coalescencia parece ser seguro para subprocesos (ver Phil Haack's analysis). Sin embargo, parece que no garantiza contra las posibles llamadas múltiples al constructor StringBuilder.

FIN EDITAR

Usted tiene un problema más grande con rosca, y es que la propia propiedad del constructor representa el estado que pueden ser compartidos a través de las discusiones. Incluso si hace que el subproceso de inicialización diferido sea seguro, no hay garantía de que los métodos que consumen el Creador lo hagan de manera segura.

// below code makes the getter thread safe 
private object builderConstructionSynch = new object(); 
public StringBuilder Builder 
{ 
    get 
    { 
     lock (builderConstructionSynch) 
     { 
      if (_builder == null) _builder = new StringBuilder(); 
     } 
     return _builder; 
    } 
} 

Lo anterior evitará el problema de roscado en la inicialización perezosa de _builder, pero a menos que sincronice sus llamadas a los métodos de instancia de StringBuilder, usted no está garantizada la seguridad de rosca en cualquiera de los métodos que consumen la propiedad del constructor. Esto se debe a que los métodos de instancia en StringBuilder no fueron diseñados para ser seguros para subprocesos. Vea el texto a continuación del MSDN StringBuilder page.

estáticos públicos (Shared en Visual Basic) de este tipo son el hilo seguro. Los miembros de instancia no son garantizados para ser seguro para subprocesos.

Si consume StringBuilder en varios subprocesos, probablemente será mejor que lo encapsule en su clase. Hacer constructor privado y exponer lo que necesita el comportamiento como un método público:

public void AppendString(string toAppend) 
{ 
    lock (Builder) 
    { 
     Builder.Append(toAppend); 
    } 
} 

De esta manera usted no está escribiendo código de sincronización por todo el lugar.

+0

Solo pensé que ?? es la operación atómica. ? ¿no es seguro para los hilos también? –

+3

No puedo decir si el operador de fusión nula es atómico, pero mi aseveración es que usted tiene un problema mayor ya que StringBuilder no es intrínsecamente seguro para subprocesos. –

+1

Consulte la respuesta editada para obtener información acerca de la seguridad de los hilos del operador nulo-coalescente (crédito para Phil Haack). Es seguro para subprocesos, ya que no crea condiciones de carrera, pero podría terminar con dos instancias separadas de Creador si las condiciones son perfectas. –

8

NO para ambas versiones

10

que no más o es menos seguro para subprocesos; aún podría tener dos hilos que hagan la comprobación nula al mismo tiempo, así crear objetos separados y no ver el otro.

+0

¿Cuál es su opinión sobre el uso de Interlocked.CompareExchange (ref _builder, new StringBuilder(), null)? – LBushkin

2

Las respuestas dadas son correctas, ambos están sin protección por hebras. De hecho, son en su mayoría equivalentes, y el operador ?? es solo magia de compilación para hacer que el código sea más eficiente. Necesita usar algún mecanismo de sincronización si desea que esto se convierta en seguro para la tarea.

2

no han probado este enfoque a mí mismo, pero si quieres hilo de seguridad sin el añadido de un esquema de bloqueo y no se preocupa de lo que podría crear y descartar una instancia de objeto, puede probar esto:

using System.Threading; 

public StringBuilder Builder 
{ 
    get 
    { 
     if (_builder != null) 
      Interlocked.CompareExchange(ref _builder, new StringBuilder(), null); 
     return _builder; 
    } 
} 

La llamada a CompareExchange() realizará un reemplazo atómico del valor en _builder con una nueva instancia de StringBuilder solo si _builder == null.Todos los métodos de la clase Interbloqueados están garantizados para que los interruptores de subprocesos no puedan adelantarse.

+0

Por cierto, probablemente sea una mala idea compartir una instancia de un StringBuilder entre hilos. SB no es intrínsecamente seguro para subprocesos, y no está claro, incluso si lo fuera, que podría hacer algo significativo con él a través de subprocesos que no están sincronizados. – LBushkin

Cuestiones relacionadas