You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

CLR via C#中的“Interlocked通用模式”是否非线程安全?

Interlocked通用模式的原子性疑问解答

核心结论

这个模式并没有失去原子性,它的原子性体现在整个CAS(CompareExchange)循环的重试逻辑上——单次读取target的操作确实是非原子的,但循环机制保证了最终的更新操作是线程安全且无丢失更新的。

先明确模式的核心逻辑

Jeffrey Richter在《CLR via C#》第4版中给出的Interlocked通用Maximum示例代码大致如下:

public static void Maximum(ref Int32 target, Int32 value) {
    Int32 currentVal = target;
    while (currentVal < value) {
        Int32 oldVal = Interlocked.CompareExchange(ref target, value, currentVal);
        if (oldVal == currentVal) break;
        currentVal = oldVal;
    }
}

这个逻辑的关键是CAS循环:

  • 先读取target的当前值到currentVal
  • 判断是否需要执行更新(这里是当currentVal小于传入的value时)
  • 调用CompareExchange:仅当target的当前值与currentVal完全一致时,才将其替换为value
  • 如果CAS操作失败(返回的oldVal不等于currentVal,说明其他线程已修改target),就用最新的oldVal更新currentVal,重新进入循环重试

关于LINQPad示例中返回100的解释

你遇到的Maximum返回其他线程修改后的100,恰恰是模式正常工作的表现:

  1. 线程A读取target为某个值(比如50)
  2. 线程B抢先将target修改为100
  3. 线程A执行CAS时,发现target已不是50,于是获取到最新的100作为currentVal
  4. 由于100不小于传入的目标值(假设是80),循环直接退出,最终target保留100——这正是该模式的设计目的:确保target最终是所有线程操作后的最大值,不会出现并发修改导致的更新丢失。

与事件add/remove实现的差异

C#编译器生成的事件add/remove方法采用了类似的CAS循环,但直接访问字段而非使用ref参数。两者的核心逻辑完全一致:都是通过CAS重试来处理并发修改,参数传递方式的差异不影响原子性的本质。

误区澄清

你提到的Int32 currentVal = target操作非原子是事实,但这个模式的原子性并非指单次读取-修改-写入的原子性,而是指最终的更新操作是原子性的,且不会丢失任何线程的修改意图。CAS循环的意义就在于:如果在读取和更新之间有其他线程修改了值,就重新读取最新值再尝试,直到成功或无需更新为止。

简言之,这个模式保证的是「要么基于最新值完成更新,要么重试」,不会出现多线程并发修改导致的中间值丢失问题。

内容的提问来源于stack exchange,提问作者Ivan Petrov

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.23 17:23:12