Interlocked.Increment 能否直接用于高并发计数?
能,而且是 .NET 中最轻量、最安全的整型原子递增方案。它底层调用 CPU 的
XADD或
LOCK XADD指令,不依赖锁、不触发上下文切换,适合每秒数万甚至百万级的简单计数场景。
但要注意:
Interlocked.Increment只支持
int和
long(含有符号),不能用于
uint、
ulong或自定义类型;返回值是递增后的值(不是原值),这点常被误用。
正确用法:参数必须是 ref int,不能传常量或表达式
常见错误是把变量名写错、传了计算结果、或用了属性——这些都会编译失败或运行时报
CS0206 “无法将属性用作 ref 或 out 参数”。
Interlocked.Increment的参数类型是
ref int,必须传一个可寻址的变量地址 不能写成
Interlocked.Increment(counter + 1)或
Interlocked.Increment(obj.Count)(如果
Count是属性) 静态字段、实例字段、局部变量(需固定)都可,但局部变量在异步/闭包中要小心生命周期
private static int _requestCount = 0;
// ✅ 正确:传字段地址
public static int RecordRequest() => Interlocked.Increment(ref _requestCount);
// ❌ 编译错误:不能将属性作为 ref 参数
// public int Count { get; private set; }
// Interlocked.Increment(ref Count); // CS0206
// ❌ 运行时可能出问题:局部变量被闭包捕获且未固定
// int local = 0;
// Task.Run(() => Interlocked.Increment(ref local)); // local 可能已被回收
和 lock 对比:什么情况下不该用 Interlocked.Increment
当计数逻辑不止“加一”,还涉及条件判断、多变量联动、或需要读-改-写复合操作时,
Interlocked.Increment就不够用了。它只保证单条指令的原子性,不提供事务语义。 需要“如果小于阈值才加一” → 得用
Interlocked.CompareExchange循环重试 要同时更新计数器和时间戳 →
Interlocked系列无直接支持,得用
lock或
SpinLock计数器需持久化到 DB 或发消息 → 原子性边界已超出内存,必须靠更高层协调 频繁调用
Interlocked.Read读取
long(尤其在 32 位进程)→ 必须用
ref long,否则可能读到撕裂值
性能关键点:避免无意中引入锁或 GC 压力
Interlocked.Increment本身零分配、无锁、无同步块开销,但周边代码容易拖慢它: 别在循环里反复调用
Console.WriteLine或字符串拼接来打印计数器——I/O 和 GC 会吃掉所有并发收益 不要用
Interlocked.Increment更新
volatile字段——
volatile不提供原子性,反而可能掩盖竞争,纯属冗余 若计数器要跨进程共享(如多个 ASP.NET Core 实例),
Interlocked完全无效,得换 Redis 或数据库 在非常高的吞吐下(如 >500K ops/sec),注意 CPU 缓存行伪共享(false sharing):把计数器和其他频繁修改的字段分开,避免同在一个 64 字节缓存行
真正容易被忽略的是:
Interlocked.Increment很快,但你的监控埋点、日志聚合、指标上报,往往才是瓶颈所在。
