c# C#中的CAS(Compare-and-Swap)操作和Interlocked类的关系

16次阅读

interlocked类是C#中CAS操作的官方实现,底层映射CPU指令;CompareExchange是最贴近经典CAS语义的核心方法,返回操作前旧值,需ref传递,支持int/long/引用类型,但不支持short/byte直接操作,且引用类型比较基于引用相等性。

c# C#中的CAS(Compare-and-Swap)操作和Interlocked类的关系

Interlocked类就是C#中CAS操作的官方实现载体

C#不提供裸露的CAS指令语法(比如像javaUnsafe.compareAndSwapInt),所有线程安全的“读-改-写”原子操作都必须通过Interlocked类完成。它底层直接映射到CPU的CMPXCHG等指令,在x86/x64上是真正的硬件级CAS,不是软件模拟。

Interlocked.CompareExchange是最贴近CAS语义的核心方法

Interlocked.CompareExchange就是C#里唯一对应经典CAS三参数模型(地址、预期值、新值)的API。它返回旧值,并仅在旧值等于预期值时才写入新值。

int value = 100; int expected = 100; int newValue = 200;  // 原子执行:如果value当前是100,则设为200,返回100;否则返回当前实际值 int oldValue = Interlocked.CompareExchange(ref value, newValue, expected);
  • 返回值是内存位置**操作前的实际值**,不是布尔结果——这是初学者最常误解的一点
  • 必须用ref传递变量地址,不能传值或属性(属性不是存储位置)
  • 支持intlongIntPtr、引用类型(Object泛型T,T需为引用类型)
  • shortbyte等小整型没有直接重载,需转为int再操作,或用Interlocked.Add等替代

CompareExchange和Add/Increment等其他Interlocked方法的区别

Interlocked.AddInterlocked.Increment等看似更“高级”的方法,其实内部仍基于CAS循环(自旋)实现,只是封装了常见模式。它们不是独立原子指令,而是CompareExchange的衍生用法。

  • Interlocked.Increment(ref i) ≈ 循环调用CompareExchange直到成功,避免手动写CAS自旋逻辑
  • 但如果你需要“比较后条件性更新+获取旧值”这个完整语义,只能用CompareExchange,其他方法不返回原值
  • 性能上,简单计数场景用Increment更简洁;复杂条件更新(如无锁Push/Pop、状态机跃迁)必须用CompareExchange

容易踩的坑:引用类型CAS的陷阱

对引用类型使用CompareExchange时,比较的是**引用相等性(reference equality)**,不是内容相等。且T必须是引用类型(或用class约束的泛型)。

var obj1 = new object(); var obj2 = new object(); object current = obj1;  // 下面这行永远失败:obj2 != obj1(引用不同),即使它们都是空object() Interlocked.CompareExchange(ref current, obj2, obj1); // 返回obj1,current不变  // 正确用法:用同一个引用做预期值 object expected = obj1; Interlocked.CompareExchange(ref current, new object(), expected);
  • 不能对值类型(如Struct)直接使用泛型CompareExchange,会编译报错
  • 不要把CompareExchange当成“线程安全的赋值”——它只在条件满足时才写,不满足就什么也不做
  • 在高竞争场景下,CompareExchange可能多次失败,需配合循环逻辑(如无锁队列中的retry loop)

真正难的从来不是调用Interlocked,而是设计出正确、可终止、无ABA问题的无锁算法结构——库函数只提供砖块,砌墙还得自己画图。

text=ZqhQzanResources