todos sirve significado reservadas que para palabras los lenguaje identificadores comandos codigos claves c# .net multithreading volatile memory-barriers

c# - sirve - Garantía de frescura variable en.NET(lectura volátil vs. lectura volátil).



todos los comandos de c# (2)

He leído mucha información contradictoria (msdn, SO, etc.) sobre volatile y VoletileRead (ReadAcquireFence).

Entiendo la implicación de restricción de reordenamiento de acceso a la memoria de esos (lo que aún me confunde por completo es la garantía de frescura), lo cual es muy importante para mí.

msdn doc para menciones volátiles :

(...) Esto garantiza que el valor más actualizado esté presente en el campo en todo momento.

msdn doc para campos volátiles menciona:

Una lectura de un campo volátil se llama lectura volátil. Una lectura volátil tiene "adquirir semántica"; es decir, se garantiza que ocurra antes de cualquier referencia a la memoria que ocurra después de esta en la secuencia de instrucciones.

El código .NET para VolatileRead es:

public static int VolatileRead(ref int address) { int ret = address; MemoryBarrier(); // Call MemoryBarrier to ensure the proper semantic in a portable way. return ret; }

Según msdn MemoryBarrier doc. La barrera de la memoria evita que se reordenen. Sin embargo, esto no parece tener ninguna implicación en la frescura, ¿correcto?

¿Cómo entonces uno puede obtener garantía de frescura? ¿Y hay alguna diferencia entre marcar el campo volátil y acceder a él con VolatileRead y VolatileWrite semántica? Actualmente estoy haciendo lo último en mi código de rendimiento crítico que necesita garantizar la frescura, sin embargo, los lectores a veces obtienen un valor obsoleto. Me pregunto si marcar el estado de volatilidad hará que la situación sea diferente.

EDIT1:

Lo que estoy tratando de lograr: obtenga la garantía de que los subprocesos de los lectores obtendrán el valor más reciente de la variable compartida (escrita por varios escritores) como sea posible; idealmente, no es mayor que el costo del cambio de contexto u otras operaciones que puedan posponer lo inmediato escribir de estado

Si la construcción de nivel volátil o superior (p. Ej., Bloqueo) tiene esta garantía (¿lo tienen?) Que cómo lo logran?

EDIT2:

La pregunta muy condensada debería haber sido: ¿cómo puedo obtener una garantía de valor tan fresco durante las lecturas como sea posible ? Idealmente sin bloqueo (ya que no es necesario el acceso exclusivo y existe un alto nivel de contención).

De lo que aprendí aquí me pregunto si esta podría ser la solución (la línea de resolución (?) Está marcada con un comentario):

private SharedState _sharedState; private SpinLock _spinLock = new SpinLock(false); public void Update(SharedState newValue) { bool lockTaken = false; _spinLock.Enter(ref lockTaken); _sharedState = newValue; if (lockTaken) { _spinLock.Exit(); } } public SharedState GetFreshSharedState { get { Thread.MemoryBarrier(); // <---- This is added to give readers freshness guarantee var value = _sharedState; Thread.MemoryBarrier(); return value; } }

La llamada MemoryBarrier se agregó para asegurarse de que ambas, lecturas y escrituras, estén envueltas con cercas completas (igual que el código de bloqueo, como se indica aquí http://www.albahari.com/threading/part4.aspx#_The_volatile_keyword ''Barreras de memoria y bloqueo '' sección)

¿Esto parece correcto o es defectuoso?

EDIT3:

Gracias a las discusiones muy interesantes que aprendí aquí, aprendí bastantes cosas y, de hecho, pude analizar la pregunta simplificada e inequívoca que tengo sobre este tema. Es bastante diferente del original, por lo que prefiero publicar uno nuevo aquí: Barrera de memoria vs Impacto entrelazado en la memoria caché Tiempo de coherencia


Creo que esta es una buena pregunta. Pero, también es difícil de responder. No estoy seguro de poder darle una respuesta definitiva a sus preguntas. No es tu culpa realmente. Es solo que el tema es complejo y realmente requiere conocer detalles que tal vez no sea posible enumerar. Honestamente, parece que ya te has educado bastante bien sobre el tema. He pasado mucho tiempo estudiando el tema yo mismo y todavía no entiendo completamente todo. Sin embargo, igual intentaré una especie de respuesta aquí.

Entonces, ¿qué significa para un hilo leer un valor nuevo de todos modos? ¿Significa que se garantiza que el valor devuelto por la lectura no sea mayor de 100 ms, 50 ms o 1 ms? ¿O significa que el valor es el último absoluto? ¿O significa que si se producen dos lecturas consecutivas, entonces se garantiza que la segunda obtendrá un valor más nuevo asumiendo que la dirección de la memoria cambió después de la primera lectura? ¿O significa algo completamente diferente?

Creo que va a tener dificultades para que sus lectores funcionen correctamente si está pensando en cosas en términos de intervalos de tiempo. En vez de eso, piensa en las cosas en términos de lo que sucede cuando encadenas las lecturas. Para ilustrar mi punto, considere cómo implementaría una operación de tipo interbloqueo utilizando una lógica arbitrariamente compleja.

public static T InterlockedOperation<T>(ref T location, T operand) { T initial, computed; do { initial = location; computed = op(initial, operand); // where op is replaced with a specific implementation } while (Interlocked.CompareExchange(ref location, computed, initial) != initial); return computed; }

En el código anterior, podemos crear cualquier operación de tipo interbloqueo si explotamos el hecho de que se garantizará que la segunda lectura de la location través de Interlocked.CompareExchange devolverá un valor más nuevo si la dirección de memoria recibió una escritura después de la primera lectura. Esto se debe a que el método Interlocked.CompareExchange genera una barrera de memoria. Si el valor ha cambiado entre lecturas, entonces el código gira alrededor del bucle repetidamente hasta que la location deje de cambiar. Este patrón no requiere que el código use el valor más reciente o más reciente ; sólo un valor más nuevo . La distinción es crucial. 1

Una gran cantidad de código de bloqueo que he visto funciona en este principio. Es decir, las operaciones generalmente se envuelven en bucles, de modo que la operación se reintenta continuamente hasta que tiene éxito. No asume que el primer intento es usar el último valor. Tampoco asume que cada uso del valor sea el último . Solo asume que el valor es más nuevo después de cada lectura.

Intenta repensar cómo deben comportarse tus lectores. Trate de hacerlos más agnósticos sobre la edad del valor. Si eso simplemente no es posible y todas las escrituras deben ser capturadas y procesadas, entonces puede que se vea obligado a adoptar un enfoque más determinista, como poner todas las escrituras en una cola y hacer que los lectores las retiren una por una. Estoy seguro de que la clase ConcurrentQueue ayudaría en esa situación.

Si puede reducir el significado de "nuevo" a solo "más nuevo", Thread.MemoryBarrier una llamada a Thread.MemoryBarrier después de cada lectura, use Volatile.Read , use la palabra clave volatile , etc., garantizará absolutamente que una lectura en una secuencia volverá. un valor más nuevo que una lectura anterior.

1 El problema ABA abre una nueva lata de gusanos.


Una barrera de memoria proporciona esta garantía. Podemos derivar la propiedad de "frescura" que usted está buscando a partir de las propiedades de redacción que garantiza una barrera.

Por frescura, probablemente significa que una lectura devuelve el valor de la escritura más reciente.

Digamos que tenemos estas operaciones, cada una en un hilo diferente:

x = 1 x = 2 print(x)

¿Cómo podríamos imprimir un valor distinto de 2? Sin volatilidad, la lectura puede moverse una ranura hacia arriba y regresar 1. Sin embargo, la volatilidad evita reordenamientos. La escritura no puede retroceder en el tiempo.

En resumen, volatile te garantiza ver el valor más reciente.

Hablando estrictamente, tendría que diferenciar entre la barrera de la memoria y la volatilidad. Este último es una garantía más fuerte. He simplificado esta discusión porque volatile se implementa usando barreras de memoria, al menos en x86 / x64.