内存屏障解决多线程下指令重排与内存可见性问题,尤其在弱内存模型CPU上防止读写乱序导致状态不一致。

内存屏障解决什么问题
多线程环境下,volatile 修饰的字段只能防止编译器重排序和部分 CPU 重排序,但无法阻止所有指令重排,尤其在弱内存模型 CPU(如 ARM、ARM64)上,读写操作可能被乱序执行,导致其他线程看到不一致的状态。内存屏障就是用来显式插入同步点,强制约束指令执行顺序和内存可见性。
MemoryBarrier 和 Thread.MemoryBarrier 的区别
Thread.MemoryBarrier() 是 .NET Framework 2.0 引入的全屏障(full fence),它同时禁止编译器和 CPU 的读写重排:前面的读写不能移到屏障后,后面的读写也不能移到屏障前。而 Thread.MemoryBarrier() 在 .NET Core / .NET 5+ 中已被标记为过时,推荐使用更明确的 Thread.VolatileRead() / Thread.VolatileWrite() 或 Interlocked 系列操作。
真正底层、仍在使用的屏障是 Thread.MemoryBarrier() 对应的 IL 指令 monitorenter / monitorexit 隐含的语义,以及 Interlocked 方法内部隐含的屏障——例如 Interlocked.CompareExchange() 在 x86 上会生成 lock cmpxchg,天然带全屏障;在 ARM64 上则会插入 dmb ish 指令。
-
Thread.MemoryBarrier():已过时,仅用于兼容旧代码,不推荐新项目使用 -
Thread.VolatileRead(ref int)/Thread.VolatileWrite(ref int, int):分别提供 acquire-load 和 release-store 语义,比全屏障轻量 -
Interlocked.*():如Interlocked.Increment(),不仅原子,还自带 full barrier,适合需要修改+同步的场景
volatile 字段 + 内存屏障的典型误用
很多人以为给字段加 volatile 就能安全实现双检锁(Double-Check Locking),但这是错的。比如下面这个单例模式片段:
private static volatile Singleton _instance; private static readonly object _lock = new object();public static Singleton Instance { get { if (_instance == null) { lock (_lock) { if (_instance == null) { _instance = new Singleton(); // ⚠️ 构造函数可能被重排到赋值之后! } } } return _instance; } }
问题在于:即使 _instance 是 volatile,C# 编译器和 CPU 仍可能把 new Singleton() 的三步(分配内存 → 调用构造函数 → 赋值给 _instance)重排成「分配 → 赋值 → 构造」,导致其他线程拿到一个未完全初始化的对象。
正确做法是用 Interlocked.CompareExchange() 或 C# 6+ 的 Lazy(其内部使用了正确的屏障):
private static readonly Lazy_lazy = new Lazy (() => new Singleton(), isThreadSafe: true); public static Singleton Instance => _lazy.Value;
何时该手动插入屏障(极少需要)
绝大多数情况你不需要手写 Thread.MemoryBarrier()。现代 C# 提供了更高层、更安全的抽象:
- 用
Interlocked替代自增/比较交换 - 用
ConcurrentQueue/ConcurrentDictionary替代手动加锁+屏障 - 用
ManualResetEventSlim或SpinWait替代忙等+裸屏障 - 只有在极少数性能敏感、且必须绕过 .NET 同步原语(如实现无锁队列)时,才需结合
Unsafe、Interlocked和平台相关屏障(如Thread.MemoryBarrier()或Atomic.Read/Write在 .NET 8+)
真正容易被忽略的是:ARM64 上 volatile 的语义比 x64 更弱,仅保证单个读/写不被重排,不保证前后访存顺序——这意味着依赖 volatile 实现状态协同的代码,在跨平台部署时可能突然出错。









