2009-01-12 14 views
27

Las cadenas en C# son inmutables y seguras para la rosca. Pero, ¿qué sucede cuando tienes una propiedad getter pública? De esta manera:¿Es una propiedad de cadena en sí misma threadsafe?

public String SampleProperty{ 
    get; 
    private set; 
} 

Si tenemos dos hilos y la primera está llamando 'get' y el segundo está llamando 'conjunto' en el "mismo" tiempo, ¿qué pasará?

mi humilde opinión, el conjunto debe hecho un bloqueo a ser flujos seguros como esto:

private string sampleField; 
private object threadSafer = new object(); 

public String SampleProperty{ 
    get{ return this.sampleField; } 
    private set{ 
     lock(threadSafer){ 
      sampleField = value; 
     } 
    } 
} 
+0

El "requisito" es: Todos los hilos que usan (leen) la propiedad deben tener el mismo/el más actualizado valor. Pero solo el objeto mismo modifica el valor. La palabra clave 'volátil' debería garantizar esto, ¿o no? – TomTom

Respuesta

37

La mayoría de las respuestas están usando la palabra "atómico" como si fueran solo cambios atómicos. No son, por lo general.

Esto se ha mencionado en los comentarios, pero no suele aparecer en las respuestas; esa es la única razón por la que he proporcionado esta respuesta. (El punto sobre el bloqueo con una granularidad más gruesa, para permitir cosas como anexar, es totalmente válida también.)

Por lo general quieren un hilo de lectura para ver el último valor de la variable/propiedad. Eso isn't guaranteed by atomicity. Como un ejemplo rápido, aquí es una mala manera para detener un hilo:

class BackgroundTaskDemo 
{ 
    private bool stopping = false; 

    static void Main() 
    { 
     BackgroundTaskDemo demo = new BackgroundTaskDemo(); 
     new Thread(demo.DoWork).Start(); 
     Thread.Sleep(5000); 
     demo.stopping = true; 
    } 

    static void DoWork() 
    { 
     while (!stopping) 
     { 
       // Do something here 
     } 
    } 
} 

DoWork bien puede bucle para siempre, a pesar de la escritura a ser atómica la variable booleana - no hay nada para detener el JIT de almacenamiento en caché el valor de stopping en DoWork. Para solucionar esto, debe bloquear, hacer la variable volatile o usar una barrera de memoria explícita. Esto también se aplica a las propiedades de cadena.

+0

Genial. Eso es lo que quería escuchar. – TomTom

15

de un campo de tipo de referencia get/set (ldfld/stfld) es (IIRC) garantiza que sea atómica, por lo que hay no debería haber ningún riesgo de corrupción aquí. Por lo que debe ser flujos seguros desde el ángulo que, pero personalmente me gustaría bloquear los datos a un nivel superior - es decir,

lock(someExternalLock) { 
    record.Foo = "Bar"; 
} 

o tal vez:

lock(record.SyncLock) { 
    record.Foo = "Bar"; 
} 

Esto le permite realizar múltiples lee/actualiza el mismo objeto como una operación atómica, por lo que otros subprocesos no pueden obtener un estado de objeto no válido

+0

TomTom: como señaló Andreas Huber en un comentario en mi respuesta, el bloqueo ocurre directamente, sin embargo, no lo protegerá de un punto donde dos hilos intentan modificar la propiedad, por lo que Marc propone bloquear la propiedad (el nivel más alto), no el campo (nivel más bajo). –

+0

(fuera del tema, pero agradecería los comentarios de quien haya donado ese voto negativo; no lo tomaré personalmente; si hay un problema en la respuesta, por favor, avíseme) –

+0

Marc: Mi "requisito" es que todos los hilos que acaban de leer el valor siempre tengan el valor más actualizado. Solo el objeto "origen" debe manipular el valor (por lo tanto, el establecedor privado). Entonces, la palabra clave "volátil" debería ajustarse a mis necesidades. Ojalá. – TomTom

4

Establecer la cadena es una operación atómica, es decir, obtendrá la n una cuerda o una cuerda vieja, nunca tendrás basura.

Si está trabajando, p. Ej.

obj.SampleProperty = "Dear " + firstName + " " + lastName; 

luego concatenar cadena de todo esto sucede antes de la llamada para establecer, por lo tanto, sampleField siempre bien ser la nueva cadena o la edad.

Sin embargo, si su código de concatenación de cadenas es autorreferencial, p.

obj.SampleProperty += obj.SampleProperty + "a"; 

y en otro lugar en otro hilo que tienen

obj.SampleProperty = "Initial String Value"; 

entonces usted necesita la cerradura.

Considere que está trabajando con un int. Si está asignando al int, y cualquier valor que obtenga del int es válido, entonces no necesita bloquearlo.

Sin embargo, si el int guarda la cuenta de la cantidad de widgets procesados ​​por dos o más hilos, para que el recuento sea preciso, debe bloquear el int. Es la misma situación para las cadenas.

Tengo la sensación de que no lo he explicado muy bien, espero que ayude.

Gracias

BW

+1

de bloqueo en el nivel ha propuesto el PO no le protegerá de los resultados inesperados con obj.SampleProperty + = "a"; Con esta operación se toma una cerradura para obtener la propiedad, a continuación, el bloqueo se libera, a continuación, una nueva cadena se crea con "un" añadido, a continuación, la cerradura se toma de nuevo para establecer la cadena. –

+0

Buen punto, esto es cierto. Supongo que aquí es donde apunta Marc Gravells al bloquear la asignación a la propiedad, no la asignación al campo. +1 a Marc (parece que hay una razón por la que está en + 20k rep) –

0

Ésta es seguro para subprocesos sin necesidad de bloqueo. Las cadenas son tipos de referencia, por lo que solo se modifica una referencia a la cadena. Las referencias de un tipo tienen la garantía de ser atómicas (Int32 en sistemas de 32 bits e Int64 en 64 bits).

0

Su segundo ejemplo de código es definitivamente no es correcto, porque los bloqueos sólo tienen el efecto deseado cuando se utilizan en todos lugares donde se accede a la variable (tanto para obtener y conjunto), por lo que el get también necesitaría una cerradura.

Sin embargo, al obtener y establecer un campo de tipo de referencia como una propiedad como esta, agregar una declaración de bloqueo no agrega ningún valor. Se garantiza que las asignaciones a los punteros sean atómicas en el entorno .NET, y si múltiples hilos están cambiando una propiedad, de todos modos tienes una condición de carrera inherente (donde los hilos pueden ver valores diferentes, esto puede o no ser un problema) entonces hay poco señalar en el bloqueo.

Entonces, para lo que hace, la primera parte del código está bien. Pero si realmente desea construir condiciones de carrera inherentes en una aplicación de subprocesos múltiples es otro asunto.

Cuestiones relacionadas