记忆障碍发生器

Oha*_*der 24 c# memory-barriers

阅读Joseph Albahari的线程教程,以下内容被提及为内存障碍的生成器:

  • C#的lock陈述(Monitor.Enter/ Monitor.Exit)
  • Interlocked班上的所有方法
  • 使用线程池的异步回调 - 包括异步委托,APM回调和任务延续
  • 设置和等待信令构造
  • 任何依赖于信令的东西,例如启动或等待任务

此外,Hans Passant和Brian Gideon 补充了以下内容(假设其中没有一个已经符合以前的类别之一):

  • 启动或唤醒线程
  • 上下文切换
  • Thread.Sleep()

我想知道这个清单是否完整(如果完整清单甚至可以实际制作)

编辑补充建议:

  • 易失性(读数意味着获取围栏,写作意味着释放围栏)

Bri*_*eon 35

以下是我对该主题的看法,并尝试在一个答案中提供准完整列表.如果我遇到任何其他人,我会不时编辑我的答案.

普遍认为会导致隐含障碍的机制:

  • 所有Monitor类方法包括C#关键字lock
  • 所有Interlocked类方法.
  • 所有Volatile类方法(.NET 4.5+).
  • 大多数SpinLock方法包括EnterExit.
  • Thread.Join
  • Thread.VolatileReadThread.VolatileWrite
  • Thread.MemoryBarrier
  • volatile关键字.
  • 任何启动一个线程或导致一个代表到另一个线程上执行,包括QueueUserWorkItem,Task.Factory.StartNew,Thread.Start,编译器提供的BeginInvoke方法等
  • 使用信令机制,诸如ManualResetEvent,AutoResetEvent,CountdownEvent,Semaphore,Barrier,等.
  • 使用编组操作,如Control.Invoke,Dispatcher.Invoke,SynchronizationContext.Post,等.

被推测(但不确定)导致隐含障碍的机制:

  • Thread.Sleep (由我自己和其他人提出,因为使用这种方法可以修复出现内存屏障问题的代码)
  • Thread.Yield
  • Thread.SpinWait
  • Lazy<T>取决于LazyThreadSafetyMode指定的

其他值得注意的提及:

  • 默认添加和删除C#中事件的处理程序,因为它们使用lockInterlocked.CompareExchange.
  • x86商店有发布范围语义
  • 尽管ECMA规范没有强制要求,但微软实施的CLI在写入时具有发布范围语义.
  • MarshalByRefObject似乎抑制了子类中的某些优化,这可能使它看起来好像存在隐式内存屏障.感谢Hans Passant发现这一点并引起我的注意.1

1 这解释了为什么BackgroundWorker没有volatileCancellationPending属性的基础字段上正常工作.

  • 太好了!(+1)Hans Passant在他的评论中提到了上下文切换:http://stackoverflow.com/q/6574389/67824.关于事件处理程序,lock(this)实际上被替换为Interlocked实现:http://stackoverflow.com/questions/3522361/add-delegate-to-event-thread-safety/3522556#3522556 (2认同)

Eri*_*ert 11

我似乎记得Thread.VolatileRead和Thread.VolatileWrite方法的实现实际上导致完全围栏,而不是半围栏.

这是非常不幸的,因为人们可能在不知不觉中开始依赖这种行为; 他们可能已经编写了需要一个完整的栅栏方案,认为他们需要一个半围栏,认为他们得到一个半围栏,并会在一个讨厌的惊喜,如果这些方法的实现永远不会提供半围栏.

我会避免这些方法.当然,我会避免所有涉及低锁代码的内容,除了最琐碎的情况之外,还不够聪明,不能正确地编写它.

  • @dlev:完全开启的MemoryBarrier在弱内存模型中具有更强大且更昂贵的效果,而不像通常在读取volatile字段时那样只执行带有IL的加载指令. (2认同)