27

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

  • C# 的lock语句 ( Monitor.Enter/ Monitor.Exit)
  • Interlocked类的所有方法
  • 使用线程池的异步回调——包括异步委托、APM 回调和任务延续
  • 设置和等待信号结构
  • 任何依赖于信号的东西,例如启动或等待一个任务

此外,Hans Passant 和 Brian Gideon添加了以下内容(假设其中没有一个已经属于前面的类别之一):

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

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

编辑添加建议:

  • 易失性(读取意味着获取栅栏,写入意味着释放栅栏)
4

3 回答 3

38

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

普遍认为会造成隐性障碍的机制:

  • 所有Monitor类方法,包括 C# 关键字lock
  • 所有Interlocked类方法。
  • 所有Volatile类方法(.NET 4.5+)。
  • 大多数SpinLock方法包括EnterExit
  • Thread.Join
  • Thread.VolatileReadThread.VolatileWrite
  • Thread.MemoryBarrier
  • volatile关键字。
  • 启动线程或导致委托在另一个线程上执行的任何内容,包括、、、QueueUserWorkItem编译器提供的方法等。Task.Factory.StartNewThread.StartBeginInvoke
  • 使用信号机制,例如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 规范没有强制要求,Microsoft 对 CLI 的实现已经在写入时释放了栅栏语义。
  • MarshalByRefObject似乎抑制了子类中的某些优化,这可能使它看起来好像存在隐式内存屏障。感谢Hans Passant发现这一点并引起我的注意。1

1这解释了为什么在没有属性的基础字段的BackgroundWorker情况下可以正常工作。volatileCancellationPending

于 2011-08-03T19:39:28.207 回答
12

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

这是非常不幸的,因为人们可能会在不知不觉中依赖这种行为;他们可能编写了一个需要完整栅栏的程序,认为他们需要半栅栏,认为他们正在获得半栅栏,并且如果这些方法的实现确实提供了半栅栏,那么他们将会大吃一惊。

我会避免这些方法。当然,我会避免所有涉及低锁定代码的事情,除了最琐碎的情况外,我不够聪明,无法正确编写代码。

于 2011-07-05T15:41:36.070 回答
3

volatile关键字也充当内存屏障。请参阅http://blogs.msdn.com/b/brada/archive/2004/05/12/130935.aspx

于 2011-07-05T11:39:56.467 回答