2010-09-09 12 views
15

actualización: Me tropecé con esto en Eric Lippert's answer-another question (que está citando la especificación):¿Está leyendo un doble que no es seguro para subprocesos?

Lee y escribe de otros tipos, incluyendo larga, ulong, dobles, y decimal, así como los tipos definidos por el usuario , no se garantiza que sean atómicos.

OK, así que la lectura de un double es no atómica. Esto significa que el valor podría modificarse a mitad de la lectura, ¿verdad? Entonces, ¿cómo se lee un valor double atómicamente?


noto que hay un método para Interlocked.Readlong valores. Esto tiene sentido para mí, ya que leer un valor de 64 bits debe requerir dos pasos y, por lo tanto, estar sujeto a las condiciones de carrera como cualquier otra acción no atómica.

Pero no hay valores Interlocked.Read para double, aunque System.Double tiene un valor de 64 bits.

estoy viendo un comportamiento extraño en mi programa en mi interfaz gráfica de usuario, que muestra una double en un cuadro de texto, mientras que double también está siendo actualizada con frecuencia por otros hilos, está mostrando el valor correcto (en las proximidades de 200,0) la mayoría de las veces, y luego muestra aleatoriamente un valor erróneo (como -0.08) ocasionalmente.

Tal vez este es un problema de rosca, o tal vez algo más. Pero primero quería reducir las posibilidades. Entonces: ¿está leyendo un double thread-safe?

+0

Tenga en cuenta que incluso si la lectura es atómica, no lo ayudará si no lo está. – nos

+0

@nos: Correcto. Pero es extraño porque la clase 'Interlocked' * does * proporciona operaciones de escritura atómica:' Exchange' y 'CompareExchange' ambos aceptan argumentos' dobles'. –

+3

@Dan, en respuesta a la pregunta de seguimiento en su edición, vea la respuesta de Jon Skeet [aquí a otra pregunta] (http://stackoverflow.com/questions/531759/c-volatile-double/531772#531772), en el que sugiere usar 'BitConverter' para moverse entre long y double (en conjunción con' Interlocked.Read'). –

Respuesta

10

la forma habitual: control de acceso con una cerradura.

+0

Básicamente, esta parece ser la respuesta más sensata (y fue lo primero, aunque la de Eric también es bastante informativa). Estoy intrigado por la posibilidad de otros enfoques más aventureros; pero los perseguiré en mi propio tiempo y acepto que esto es lo razonable en casi todos los casos. –

16

está leyendo un hilo doble-seguro?

No. Como la especificación dice

Lee y escribe de otros tipos, incluyendo larga, ulong, dobles, y decimales, así como los tipos definidos por el usuario, no se garantiza que sea atómica.

Pasando.

Esto significa que el valor podría modificarse a mitad de lectura, ¿verdad?

Sí.

Entonces, ¿cómo se puede leer un valor doble atómicamente?

Bloquea cada acceso a la variable mutable.

Y una pregunta que no pidió, pero a menudo se le preguntó como un seguimiento de sus preguntas:

Si realizo un campo "volátil" make lee/escribe de ella atómica?

No. No es legal crear un campo volátil de tipo double.

2

El CLR solo promete una alineación variable de 4. Lo que significa que es muy posible que un largo o doble se ubiquen a horcajadas sobre los límites de una línea de caché de la CPU. Eso hace que la lectura garantizada sea no atómica.

También es un problema de perfusión bastante serio, leer una variable tan mal alineada es más de 3 veces más lenta. Nada que puedas hacer al respecto más allá de piratear punteros.

+1

Si falta una caché en la instrucción x86 fld, no estoy seguro de si la CPU permitirá una carga parcial en el registro FPU. Mi suposición es que la CPU en sí hará algo de enclavamiento para preservar la atomicidad de las operaciones de FPU. No he encontrado que los documentos de Intel prueben o desmientan esto. Aún así, incluso si las CPU Intel hacen esto, otras no. –

5

Use Interlocked.Exchange O Interlocked.CompareExchange para atomic leer como esto.

Interlocked.Exchange(ref somevariable, somevariable)

Devuelve valor original.

Si quiere evitar escribir use compareExchange.

Interlocked.CompareExchange(ref somevariable, somevalue, somevalue);

Esto reemplazará la variable con el segundo argumento si es igual al tercer argumento, y volver al valor original. Al usar el mismo valor (por ejemplo, cero) en ambos puntos, garantiza que el valor de la variable no cambia.

Cuestiones relacionadas