贝利信息

c# Interlocked 和 lock 的区别 c#无锁编程怎么实现

日期:2026-01-17 00:00 / 作者:煙雲
Interlocked专治单个变量的原子读写,lock用于多步逻辑的排他执行;前者无锁高效,后者支持复杂临界区,误用会导致性能下降或并发错误。

什么时候该用 Interlocked,而不是 lock

直接说结论:Interlocked 专治「单个变量的原子读写」,比如计数器增减、标志位设置、引用替换;lock 是为「一段逻辑的排他执行」准备的,比如更新多个字段、读-改-写复合操作、涉及 I/O 或复杂校验的临界区。

常见错误现象:有人把 Interlocked.Increment(ref _count) 换成 lock 去保护一个 _count++,结果性能掉一截还毫无必要;也有人试图用 Interlocked 去保证「先检查余额再扣款」这种两步操作的原子性,结果出现超卖——因为 Interlocked 不提供「条件+动作」的原子组合能力。

Interlocked.CompareExchange 是无锁编程的核心入口

C# 的无锁编程不是靠“不用锁”来定义的,而是靠「CAS(Compare-And-Swap)循环重试」实现的乐观并发控制。而 Interlocked.CompareExchange 就是这个机制的唯一公开出口。

它签名是:Interlocked.CompareExchange(ref int location1, int value, int comparand) —— 只有当 location1 == comparand 时,才把 value 写入,并返回旧值;否则不写,只返回当前值。整个过程原子。

private int _state = 0; // 0=ready, 1=busy
public bool TryEnter()
{
    return Interlocked.CompareExchange(ref _state, 1, 0) == 0;
}
// 成功返回 true,且 _state 已设为 1;失败说明已被别人抢先设为 1

lockInterlocked 性能差距有多大?

在高竞争、高频更新场景下,差距非常真实:10 个线程对同一计数器做 100 万次递增,Interlocked.Increment 通常比 lock 快 3–5 倍,且 CPU 时间更稳,不会因线程挂起/唤醒抖动。

但这个优势只在「简单操作」上成立。一旦你把 Interlocked 套进复杂逻辑里强行“无锁”,比如在 CAS 循环里调用数据库、做字符串拼接、访问非原子字段,性能反而更差——因为自旋浪费 CPU,且逻辑本身已失去原子性保障。

最容易被忽略的坑:Interlocked 不保顺序,也不保可见性之外的语义

很多人以为用了 Interlocked 就万事大吉

,结果发现日志乱序、状态跳变、甚至偶发 null 引用——问题往往出在「它只管那个变量本身,不管其他东西」。

真正难的从来不是“怎么写无锁”,而是“怎么证明它在所有路径下都正确”。哪怕只多一步判断,就很可能得退回 lock