多线程环境下计数器值未达预期,如何实现线程安全递增?
修复多线程下共享计数器的竞态条件问题
你的问题根源在于_counter++并非原子操作,它在CPU层面会拆分为三个独立步骤:读取当前_counter的值、对值执行加1运算、将新值写回_counter。当多个线程同时执行这些步骤时,会出现线程间的操作覆盖——比如线程A刚读取了_counter的当前值,线程B也读取了同一个值,两者各自加1后写回,最终两次递增只生效一次,导致计数丢失。
以下是两种可靠的修复方案:
方案一:使用Interlocked类的原子操作
Interlocked类提供了一系列原生支持的原子操作,能确保读取、修改、写入的整个过程不会被其他线程打断,是简单计数器场景下最优的选择。修改后的代码如下:
public class DataProcessor { private static int _counter = 0; public async Task ProcessDataAsync() { var tasks = new List<Task>(); for (int i = 0; i < 1000; i++) { tasks.Add(Task.Run(() => IncrementCounter())); } await Task.WhenAll(tasks); } private void IncrementCounter() { // 原子递增操作,确保线程安全 Interlocked.Increment(ref _counter); } }
方案二:使用lock语句加锁
如果后续需要对共享数据执行更复杂的多步操作,lock语句会更灵活。它通过互斥锁确保同一时间只有一个线程能执行锁块内的代码,避免竞态条件。修改后的代码如下:
public class DataProcessor { private static int _counter = 0; // 声明静态只读的锁对象,确保所有线程共享同一锁,且不会被意外修改 private static readonly object _lockObject = new object(); public async Task ProcessDataAsync() { var tasks = new List<Task>(); for (int i = 0; i < 1000; i++) { tasks.Add(Task.Run(() => IncrementCounter())); } await Task.WhenAll(tasks); } private void IncrementCounter() { lock (_lockObject) { _counter++; } } }
两种方案对比
Interlocked:基于CPU原子指令实现,无线程阻塞开销,性能更高,适合简单的原子操作(如计数器增减)。lock:基于.NET的Monitor类实现,会在锁被占用时阻塞线程,开销略高,但支持复杂的多步共享数据操作。
内容的提问来源于stack exchange,提问作者Vishal Kumar Gupta
相关产品推荐
相关产品推荐

