Pregunta

Cuando escribo un valor en un campo, lo que garantiza Cómo llego con respecto a cuando el nuevo valor se guarda en la memoria principal? Por ejemplo, ¿cómo sé que el procesador no mantienen el nuevo valor en su caché privada, pero actualiza la memoria principal?
Otro ejemplo:

int m_foo;

void Read() // executed by thread X (on processor #0)
{
   Console.Write(m_foo);
}

void Write() // executed by thread Y (on processor #1)
{
   m_foo = 1;
}

¿Existe la posibilidad de que después de Write () fue terminado de ejecutarse, algunos otros se ejecuta hilo Read () , pero en realidad verán "0" como el valor actual? (Ya que tal vez la escritura previa a m_foo no se volcó todavía?).
¿Qué tipo de primitivas (al lado de las cerraduras) están disponibles para asegurar que las escrituras fueron los enrojeció?


Editar
En el ejemplo de código que he usado, la escritura y lectura se colocan en el método diferente. No sólo afectan Thread.MemoryBarrier Reording instrucción que existe en el mismo ámbito?

Además, vamos a suponer que no van a ser inline por el JIT, ¿cómo puedo estar seguro de que el valor escrito en m_foo no será almacenada en un registro, sino a la memoria principal? (O cuando se lee m_foo, no va a obtener un valor antiguo de la memoria caché de la CPU).

¿Es posible lograr esto sin el uso de bloqueos o la palabra clave 'volátil'? (También, digamos que no estoy usando los tipos primitivos, pero una palabra de tamaño estructuras [tan volátil no puede aplicarse] .)

¿Fue útil?

Solución

volátil y Interlocked ya se han mencionado, que pidió primitivas, una adición a la lista es utilizar Thread.MemoryBarrier() antes de que sus escrituras o lee. Esto garantiza que no habrá reordenamiento se hace de la memoria escribe y lee.

Esto está haciendo "a mano" lo lock, Interlocked y volatile pueden hacer automáticamente la mayoría de las veces. Usted podría utilizar esto como un reemplazo completo a cualquier otra técnica, pero es sin duda el camino más difícil viajar, y así dice MSDN:

  

"Es difícil construir programas multihilo correcto utilizando   MemoryBarrier. Para la mayoría de los propósitos, la   C # declaración de bloqueo, el Visual Basic   declaración SyncLock, y los métodos de   la clase Monitor de proporcionar más fácil y   maneras menos propensa a errores para sincronizar   los accesos a memoria. Le recomendamos que   utilizarlos en lugar de MemoryBarrier. "

¿Cómo utilizar MemoryBarrier

Un ejemplo muy fino son las implementaciones de VolatileRead y VolatileWrite, que ambos utilizan internamente MemoryBarrier. La regla básica a seguir es: cuando se lee una variable, colocar una barrera de memoria después de la lectura. Cuando se escribe el valor, la barrera de la memoria debe venir antes de la escritura.

En caso de que haya dudas sobre si esto es menos eficiente que lock, tenga en cuenta que el bloqueo es nada más que "llena de esgrima", en la que se coloca una barrera de memoria antes y después del bloque de código (Monitor ignorando por un momento). Este principio está bien explicado en este excelente artículo definitivo sobre las discusiones, de bloqueo, las barreras de memoria volátiles y por Albahari .

A partir de reflector:

public static void VolatileWrite(ref byte address, byte value)
{
    MemoryBarrier();
    address = value;
}

public static byte VolatileRead(ref byte address)
{
    byte num = address;
    MemoryBarrier();
    return num;
}

Otros consejos

Si desea asegurarse de que está escrito con prontitud y en orden, a continuación, marcarlo como volatile , o (con más dolor) utilizar Thread.VolatileRead / Thread.VolatileWrite (no es una opción atractiva, y es fácil perderse uno, por lo que es inútil).

volatile int m_foo;

De lo contrario, no tienen prácticamente ninguna garantía de nada (tan pronto como se habla de múltiples hilos).

También puede ser que desee mirar bloqueo ( Monitor ) o Interlocked , que lograría el mismo efecto mientras la mismo enfoque se utiliza a partir de todo el acceso (es decir, todos lock , o todos Interlocked, etc).

Mientras no se utiliza ninguna sincronización usted no tiene ninguna garantía de que un hilo que se ejecuta en un procesador ve los cambios realizados por otro hilo que se ejecuta en otro procesador. Eso es debido a que el valor podría ser almacenado en caché en los cachés de CPU o en un registro de la CPU.

Por lo tanto lo que necesita o bien marcar la variable como volátil. Esto creará un 'Pasa-Before'-realation entre Lee un escrituras.

Eso no es un problema de caché del procesador. Escrituras por lo general se pasan a través de (escribe ir tanto a almacenar en caché y la memoria principal) y todas las lecturas accederá a la memoria caché. Pero hay muchas otras cachés en el camino (lenguaje de programación, bibliotecas, sistema operativo, de E / S tampones, etc.). El compilador también puede optar por mantener una variable en un registro del procesador y nunca escribirlo en memoria principal (que es lo que el operador volátil está diseñado para, evitar valor de almacenar en el registro cuando puede ser una memoria de E / S mapeada).

Si tiene varios procesos o subprocesos múltiples y la sincronización es un problema que hay que hacerlo especifique lo contrario, hay muchas maneras de hacerlo dependiendo del caso de uso.

Para un solo programa de roscado, no me importa, el compilador hará lo que debe y lee accederá a lo que se ha escrito.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top