threads thread for book c# memory-barriers

for - multithreading c# book



Generadores de barrera de memoria (3)

Aquí está mi opinión sobre el tema e intentar proporcionar una lista casi completa en una respuesta. Si encuentro otros, editaré mi respuesta de vez en cuando.

Mecanismos generalmente acordados para causar barreras implícitas:

  • Todos los métodos de clase Monitor , incluido el lock palabra clave C #
  • Todos los métodos de clase entrelazados.
  • Todos los métodos de clase Volatile (.NET 4.5+).
  • La SpinLock métodos de SpinLock incluyen Enter y Exit .
  • Thread.Join
  • Thread.VolatileRead y Thread.VolatileWrite
  • Thread.MemoryBarrier
  • La palabra clave volatile .
  • Cualquier cosa que inicie un hilo o haga que un delegado se ejecute en otro hilo, incluyendo QueueUserWorkItem , Task.Factory.StartNew , Thread.Start , el compilador proporcionó los métodos de BeginInvoke , etc.
  • Usando un mecanismo de señalización como ManualResetEvent , AutoResetEvent , CountdownEvent , Semaphore , Barrier , etc.
  • Uso de operaciones de cálculo de referencias como Control.Invoke , Dispatcher.Invoke , SynchronizationContext.Post , etc.

Mecanismos que se especulan (pero no se sabe con certeza) que causan barreras implícitas:

  • Thread.Sleep (propuesto por mí mismo y posiblemente por otros debido a que el código que presenta un problema de barrera de memoria se puede solucionar con este método)
  • Thread.Yield
  • Thread.SpinWait
  • Lazy<T> según el LazyThreadSafetyMode especificado

Otras menciones notables:

  • Agregue y elimine de forma predeterminada controladores para eventos en C #, ya que usan lock o Interlocked.CompareExchange .
  • Las tiendas x86 tienen semántica de vallas de liberación
  • La implementación de Microsoft de la CLI tiene una semántica de vallas de liberación en las escrituras a pesar del hecho de que la especificación ECMA no lo exige.
  • MarshalByRefObject parece suprimir ciertas optimizaciones en subclases que pueden hacer que parezca como si estuviera presente una barrera de memoria implícita. Gracias a Hans Passant por descubrir esto y llamar mi atención. 1

1 Esto explica por qué BackgroundWorker funciona correctamente sin tener volatile en el campo subyacente para la propiedad CancellationPending .

Al leer el tutorial de enhebrado de Joseph Albahari , se menciona a los siguientes como generadores de barreras de memoria:

  • Declaración de lock C # ( Monitor.Enter / Monitor.Exit )
  • Todos los métodos en la clase Interlocked
  • Devoluciones de llamada asincrónicas que utilizan el grupo de subprocesos: estos incluyen delegados asincrónicos, devoluciones de llamada de APM y continuaciones de tareas
  • Configuración y espera en una construcción de señalización
  • Todo lo que dependa de la señalización, como iniciar o esperar en una tarea

Además, Hans Passant y Brian Gideon agregaron lo siguiente (asumiendo que ninguno de los que ya encaja en una de las categorías anteriores):

  • Comenzar o despertar un hilo
  • Cambio de contexto
  • Thread.Sleep()

Me preguntaba si esta lista estaba completa (si una lista completa podría hacerse prácticamente)

EDITAR adiciones sugeridas:

  • Volátil (la lectura implica una valla de adquisición, la escritura implica una valla de liberación)


Me parece recordar que las implementaciones de los métodos Thread.VolatileRead y Thread.VolatileWrite en realidad causan vallas completas, no medias vallas.

Esto es profundamente desafortunado, ya que las personas podrían haber llegado a confiar en este comportamiento sin saberlo; Es posible que hayan escrito un programa que requiera una valla completa, crean que necesitan una valla y creen que están recibiendo una media valla, y se encontrarán con una desagradable sorpresa si una implementación de estos métodos alguna vez proporciona una valla media.

Evitaría estos métodos. Por supuesto, evitaría todo lo relacionado con el código de bloqueo bajo, no siendo lo suficientemente inteligente como para escribirlo correctamente en cualquier cosa que no sean los casos más triviales.