内存屏障生成器
阅读Joseph Albahari的线程教程 ,以下内容被提及作为内存屏障的生成器:
- C#的
lock
语句(Monitor.Enter
/Monitor.Exit
) -
Interlocked
类的所有方法 - 使用线程池的异步回调 – 包括异步委托,APM回调和任务延续
- 设置和等待信令构造
- 任何依赖于信令的东西,例如启动或等待任务
此外,Hans Passant和Brian Gideon 补充了以下内容 (假设其中没有一个已经适合以前的类别之一):
- 启动或唤醒线程
- 上下文切换
-
Thread.Sleep()
我想知道这个清单是否完整(如果完整清单甚至可以实际制作)
编辑添加建议:
- 易失性(读数意味着获取围栏,写作意味着释放围栏)
以下是我对该主题的看法,并尝试在一个答案中提供准完整列表。 如果我遇到任何其他人,我会不时编辑我的答案。
普遍认为会导致隐含障碍的机制:
- 所有
Monitor
类方法包括C#关键字lock
- 所有
Interlocked
类方法。 - 所有
Volatile
类方法(.NET 4.5+)。 - 大多数
SpinLock
方法包括Enter
和Exit
。 -
Thread.Join
-
Thread.VolatileRead
和Thread.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
取决于指定的LazyThreadSafetyMode
其他值得注意的提及:
- 默认添加和删除C#中事件的处理程序,因为它们使用
lock
或Interlocked.CompareExchange
。 - x86商店有发布范围语义
- 尽管ECMA规范没有强制要求,但微软实施的CLI在写入时具有发布范围语义。
-
MarshalByRefObject
似乎抑制了子类中的某些优化,这可能使它看起来好像存在隐式内存屏障。 感谢Hans Passant发现这一点并引起我的注意。 1
1 这解释了为什么BackgroundWorker
在CancellationPending
属性的基础字段上没有volatile
的情况下正常工作的原因。
我似乎记得Thread.VolatileRead和Thread.VolatileWrite方法的实现实际上导致完全围栏,而不是半围栏。
这是非常不幸的,因为人们可能在不知不觉中开始依赖这种行为; 他们可能已经编写了一个需要完整围栏的程序,认为他们需要一个半围栏,认为他们正在获得一个半围栏,并且如果这些方法的实施确实提供了半围栏将会出现令人讨厌的惊喜。
我会避免这些方法。 当然,我会避免所有涉及低锁代码的事情,除了最琐碎的情况之外,不能足够智能地将其正确地写入。
volatile
关键字也充当内存屏障。 见http://blogs.msdn.com/b/brada/archive/2004/05/12/130935.aspx