c# 高并发计数器怎么实现 Interlocked.Increment

来源:这里教程网 时间:2026-02-21 17:36:41 作者:

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
很快,但你的监控埋点、日志聚合、指标上报,往往才是瓶颈所在。

相关推荐