跳转到主内容
极星编程网:以代码为星,赴技术山海!

c# SpinLock 和 lock 的区别和适用场景

SpinLock 适用于临界区极短(<2微秒)、纯内存操作且无阻塞的场景,而 lock 更适合争用高或临界区较长的情况;SpinLock 是值类型、避免上下文切换但易致高 CPU,lock 基于 Monitor、触发线程等待但开销稳定。 SpinLock 和 lock 的底层行为完全不同
lock
是语法糖,本质调用
Monitor.Enter
和
Monitor.Exit
,线程拿不到锁就立刻进入等待态,让出 CPU,触发上下文切换;而
SpinLock
是纯用户态结构,拿不到锁时线程不放弃 CPU,持续用
Interlocked.CompareExchange
检查状态——也就是“空转”,直到成功获取锁。 这意味着
lock
在争用高、临界区长时更省 CPU,但每次切换代价约 1–10 微秒
SpinLock
避免了切换开销,但若临界区超过几微秒(比如含
Thread.Sleep(1)
或 I/O),自旋反而更慢、还拖高 CPU 使用率
SpinLock
是值类型(
struct
),不能跨线程共享实例;
lock
锁的是引用对象(如
private static readonly object _lock = new object()
) 什么时候该选 SpinLock?看三个硬指标 不是“并发高就上 SpinLock”,而是必须同时满足: 临界区逻辑极短:纯内存操作,无阻塞、无调度点,执行时间稳定在 锁争用低或中等:高争用下多个线程同时自旋,CPU 白白拉满,却谁也进不去 临界区不含任何可能触发 GC 或异常传播的代码:因为
SpinLock.Enter
不是异常安全的——若临界区抛异常且没正确
Exit
,会导致死锁(同一线程后续再
Enter
就卡住) 反例:
lock
更适合大多数场景,比如数据库连接池管理、缓存写入、日志缓冲区追加——哪怕只多一行
if (obj == null) return;
,都建议别碰
SpinLock
。 Trae win Trae Windows 官方版本现已全面支持 Win10 和 Win11 系统(64位)。国内开发者可直接访问 Trae 中文官网(trae.cn 或 trae.com.cn),一键下载官方正版 .exe 安装包,享受国内直连的高速稳定体验。安装过程极其简便,双击即可运行,并支持一键无缝导入 VS Code 或 Cursor 的原有主题、快捷键及插件配置。登录支持手机号、微信或稀土掘金账号。内置免费的豆包与 DeepSeek 等顶级大模型,让你开箱即用,轻松开启高效的 AI 智能编程之旅。 下载 常见误用和崩溃现场 下面这些写法看似合理,实际会立即出问题: 把
SpinLock
实例定义成
static
并在多线程间复用:可以,但必须确保每个线程都用
ref bool lockTaken
正确配对
Enter/Exit
在
async
方法里用
SpinLock
:绝对禁止。await 后续回调可能在不同线程执行,
Exit
调用会抛
InvalidOperationException
(“只能由持有锁的线程释放”) 忘记检查
lockTaken
就直接调用
Exit
:一旦
Enter
失败(比如超时未设、或被中断),
Exit
会崩 重复
Enter
同一个
SpinLock
实例(不可重入):同一线程第二次调用
Enter
就死锁,
IsHeld
属性也无法救场
private static SpinLock _spinLock = new SpinLock(); private static int _counter = 0; public void Increment() { bool lockTaken = false; try { _spinLock.Enter(ref lockTaken); // 必须传 ref! _counter++; } finally { if (lockTaken) _spinLock.Exit(); // 必须判空! } }
性能对比不能只看吞吐,要看 CPU 效率 在 100 线程、每轮临界区仅 50 纳秒的纯计数场景下,
SpinLock
可能比
lock
快 3–5 倍;但只要临界区加入一次
Interlocked.Increment
以外的操作(比如查字典、拼字符串),优势就消失;若临界区平均耗时 > 2 微秒,
SpinLock
的 CPU 占用率常飙到 300%+,而吞吐反而更低。 调试时注意:Windows 性能监视器里看
% Processor Time
+
Context Switches/sec
,二者此消彼长 上线前务必压测:用
dotnet-trace
录制真实负载,观察
SpinLock
自旋循环是否成为 热点 (方法名含
TryEnter
或
SpinOnce
) 没有银弹:.NET 6+ 中,多数短临界区场景优先考虑
Interlocked
(如
Interlocked.Add
),它比
SpinLock
更轻、更安全、还能跨平台保证语义 真正难的不是选哪个锁,而是判断“这段代码到底算不算短临界区”——它取决于你的硬件、当前 GC 压力、甚至 JIT 编译模式。实操中,先用
lock
写稳,再用
dotnet-counters
看
monitor-lock-contention-rate
,如果每秒争用低于 10 次,基本不用换;高于 100 次且确认临界区干净,才值得尝试
SpinLock
。

相关文章