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

多线程环境下:_counter += 1 与 Interlocked.Increment(ref _counter) 该如何选择?

在已加锁的多线程场景中:_counter += 1 vs Interlocked.Increment(ref _counter)

先给明确结论:在已经通过lock(或Monitor、Mutex等互斥锁)保护的代码块里,使用_counter += 1就足够,完全安全且更推荐,具体分析如下:

核心逻辑

_counter += 1本身不是原子操作,它会拆分为「读取当前值→加1→写入新值」三个步骤。在无锁的多线程场景下,多个线程可能同时执行这些步骤,引发竞态条件(比如两个线程同时读取到同一个初始值,加1后写入,最终计数器只增加了1而非2)。

但当你用lock包裹这段代码时,锁已经保证了同一时刻只有一个线程能进入代码块执行,彻底消除了竞态条件的可能。此时_counter += 1的执行是完全串行化的,结果和原子操作一致。

为什么不推荐用Interlocked.Increment?

  • 语义冗余:加锁的核心目的就是保护共享资源的访问,此时再叠加原子操作属于重复防护,会让其他开发者产生疑惑——是不是锁的范围有漏洞?或者存在未被锁覆盖的访问路径?反而降低了代码可读性。
  • 性能无优势:Interlocked.Increment依赖系统级原子指令,而在已经加锁的场景下,锁本身的开销(线程上下文切换、内核态/用户态转换等)远大于两种计数操作的差异,性能上没有必要舍简求繁。

什么时候该用Interlocked.Increment?

如果代码没有加锁,但需要对共享计数器做线程安全的递增操作,Interlocked.Increment是更优选择——它是轻量级原子操作,不需要像lock那样产生额外的上下文开销,性能更高效。

针对你的代码示例

在FuncA的lock代码块中,直接使用_counter += 1即可,完全满足线程安全要求,代码更简洁易懂。修改后的代码如下:

public class Demo
{
    public void Main()
    {
        for (int i = 0; i < 5; i++)
        {
            new Thread(() => FuncA()).Start();
        }
    }

    private int _counter;
    public void FuncA()
    {
        lock (this)
        {
            _counter += 1; // 安全且推荐的写法
        }
    }
}

内容的提问来源于stack exchange,提问作者Alireza Molaei

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.26 09:42:13