多线程环境下:_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
相关产品推荐
相关产品推荐

