lock 的性能损耗在什么量级?
在无竞争(即几乎没线程抢锁)时,
lock的开销极低——现代 .NET(6+)会先自旋几轮,不进内核,耗时通常
典型对比(1000 万次计数操作,单核模拟竞争):
Interlocked.Increment(ref x):约 80–120 ms
lock(obj) { x++; }:约 450–900 ms(取决于争抢强度)
Mutex.WaitOne():常超 3000 ms(跨进程开销大)
什么时候该换 Interlocked 而不是 lock?
只要你的临界区只干一件事:读/写/更新一个字段(int、bool、引用等),且逻辑不依赖其他变量或状态判断,就该用
Interlocked。
常见误用场景:
❌lock保护一个
if (count > 0) count--;—— 这其实是「读-判-改」三步,
lock正确但过重 ✅ 改成
if (Interlocked.Decrement(ref count) >= 0) { /* 成功减了 */ } —— 原子 CAS,无锁,更快更安全
✅ 计数器、开关标志、引用替换(Interlocked.Exchange)、初始化单例(
Interlocked.CompareExchange)都适合
lock 真正的代价不在“加锁”,而在“阻塞”
lock本身不慢,慢的是线程被迫挂起 + 唤醒带来的上下文切换。一个被阻塞的线程要经历:用户态 → 内核态 → 睡眠队列 → 调度唤醒 → 用户态,这过程至少耗费几十微秒,还吃 CPU 调度资源。
实操建议:
避免在lock块里做 I/O、网络调用、
Thread.Sleep、复杂计算——这些会让锁持有时间变长,放大争抢 不要用
string或装箱值类型(如
(object)123)当锁对象——每次都是新对象,根本锁不住 优先用
private static readonly object _lock = new object();,别用
this或 public 成员,防外部干扰
读多写少?别硬扛 lock,试试 ReaderWriterLockSlim
如果你的字段被读取几千次,只写几次(比如配置缓存、设备状态),
lock就是瓶颈:所有读操作也得排队。
换成
ReaderWriterLockSlim后,读可以并发,写才互斥:
private readonly ReaderWriterLockSlim _rwLock = new ReaderWriterLockSlim();
private int _value;
<p>public int Value
{
get
{
_rwLock.EnterReadLock();
try { return _value; }
finally { _rwLock.ExitReadLock(); }
}
set
{
_rwLock.EnterWriteLock();
try { _value = value; }
finally { _rwLock.ExitWriteLock(); }
}
}注意:它比
lock略重(多一层状态管理),但读并发提升明显;若读写比例 Interlocked 或
volatile+ 规则约束。
真正容易被忽略的一点:锁的粒度永远比锁的种类更重要。哪怕你用了
Interlocked,如果把它套在一个高频循环里反复调用,也比把几个相关字段打包进一个轻量
lock块更慢——因为函数调用和内存屏障本身也有成本。
