2011-08-03 14 views
12

Se sabe que una referencia toma 4 bytes de memoria en procesador de 32 bits y 8 bytes en procesador de 64 bits. Por lo tanto, los procesadores garantizan que las lecturas únicas y las escrituras en la memoria en incrementos del tamaño de palabra natural de la máquina se llevarán a cabo atómicamente. Por otro lado hay 2 métodos de la clase Interlocked:Uso de Interlocked.Exchange para la actualización de referencias e Int32

public static int Exchange(
    ref int location1, 
    int value 
) 

y

public static T Exchange<T>(
    ref T location1, 
    T value 
) 
where T : class 

Entonces, la pregunta es ¿por qué es necesario para Interlocked.Exchange Int32 y para los tipos de referencia? ¿No podría hacerse de forma segura simplemente usando una asignación simple porque es atómica?

Respuesta

9

No se trata solo de atomicidad. También se trata de la visibilidad de la memoria. La variable se puede almacenar en la memoria principal o en la memoria caché de la CPU. Si la variable solo se almacena en la memoria caché de la CPU, no será visible para los hilos que se ejecutan en una CPU diferente. Considere siguiente ejemplo:

public class Test { 
    private Int32 i = 5; 

    public void ChangeUsingAssignment() { 
     i = 10; 
    } 

    public void ChangeUsingInterlocked() { 
     Interlocked.Exchange(ref i, 10); 
    } 

    public Int32 Read() { 
     return Interlocked.CompareExchange(ref i, 0, 0); 
    } 
} 

Ahora bien, si usted llama 'ChangeUsingAssignment' en un hilo y 'Leer' en otro hilo el valor de retorno puede ser de 5, no 10. Pero si se llama ChangeUsingInterlocked, 'Leer' devolverá 10 como se esperaba.

----------   ------------   ------------------- 
| CPU 1 | --> | CACHE 1 | --> |     | 
----------   ------------  |     | 
             |  RAM  | 
----------   ------------  |     | 
| CPU 2 | --> | CACHE 2 | --> |     | 
----------   ------------   ------------------- 

En el diagrama anterior método 'ChangeUsingAssignement' puede resultar en valor 10 get 'pegado' en CACHE 2 y no llegar a la RAM. Cuando la CPU 1 intente más tarde leerlo, obtendrá el valor de la RAM donde todavía está 5. Utilizando el enclavamiento en lugar de la escritura ordinaria, se asegurará de que el valor 10 llegue hasta la memoria RAM.

+0

Muchas gracias. Está lo suficientemente claro ahora. –

+3

Sé que esto es, obviamente, un año más tarde, pero si es posible, ¿podría por favor revisar esto? Este sitio web http://igoro.com/archive/volatile-keyword-in-c-memory-model-explained/ parece implicar que todas las escrituras en C# ya son volátiles. – user981225

5

El intercambio de un valor de memoria y el contenido de un registro de la CPU no es en general atómico. Ambos necesitan leer y escribir la ubicación de la memoria. Además, los métodos Interlocked garantizan que la operación sea atómica incluso en computadoras multi-core donde cada núcleo tiene su propia memoria caché y potencialmente su propia vista de la memoria principal.

+0

Pero si no necesito tener un valor anterior y solo quiero asignar un nuevo valor a alguna variable. Esto es solo escribir y atómico, ¿verdad? –

+0

@OlegDudnyk Creo que no es correcto, incluso a usted no le importa el valor original. Considere cómo lograr eso en lenguaje ensamblador, depende de dónde se encuentren su variable/valor src y dest, podrían traducirse en más de una instrucción, entre 2 instrucciones, su aplicación aún puede ser interrumpida. En algunas arquitecturas, puede bloquear el bus para asegurarse de que nadie pueda leer la memoria de escritura (por ejemplo, IA tiene el prefijo LOCK), y se garantiza que algunas instrucciones sean atómicas (por ejemplo, XCHG, CMPXCHG, etc., en IA). – codewarrior

8

Interlocked.Exchange tiene un valor de retorno, lo que le permite saber qué valor acaba de reemplazar. Es la combinación de establecer un nuevo valor y obteniendo el valor anterior que estos métodos logran.

+0

Por lo tanto, si no necesita el valor anterior, simplemente puede asignarlo, pero también necesitaría una llamada de barrera para invalidar el/los caché (s). –

+0

@Henk: ¿Podría darnos más detalles a qué se refiere con una llamada de barrera? –

+0

La referencia clásica: http: //www.albahari.com/threading/ –

4

Interlock.Exchange devuelve valor original al realizar una operación atómica. El objetivo es proporcionar un mecanismo de bloqueo. Por lo tanto, en realidad es dos operaciones: lee el valor original y establece un nuevo valor. Esos dos juntos no son atómicos.

+1

Pero si no necesito tener un valor anterior y solo quiero asignar un nuevo valor a alguna variable. Esto es solo escribir y atómico, ¿verdad? –

+0

@gorik: si es una operación de lectura o escritura en una variable de cualquier tipo de referencia, o cualquier tipo de valor incorporado que ocupe cuatro bytes o menos (para una máquina de 32 bits), entonces sí, se garantiza que será atómico . Consulte esta serie de publicaciones de Eric Lippert sobre el tema, que serán de alguna ayuda: [Atomicidad, volatilidad e inmutabilidad son diferentes, primera parte] (http://blogs.msdn.com/b/ericlippert/archive/2011/ 05/26/atomicity-volatility-and-immutability-are-different-part-one.aspx) – InBetween

Cuestiones relacionadas