CSharp多线程之RaceCondition

1. 什么是 Race Condition

当多个线程在没有同步机制的情况下同时读写共享数据,就可能导致数据不一致。
典型场景:

1
2
3
4
5
6
7
int counter = 0;

void Increment()
{
// 多个线程同时运行时,counter++ 不是原子操作
counter++;
}

可能结果并不是预期的 counter == 线程数,而是小于,因为:

  • counter++ 实际上分为三步:读取值 → 加 1 → 写回。

  • 如果两个线程同时读到相同值,最后一个写入会覆盖前一个。


2. 常见的解决方法

🔹 方法 1:lock(最常用)

1
2
3
4
5
6
7
8
9
10
private object _lockObj = new object();
int counter = 0;

void Increment()
{
lock (_lockObj)
{
counter++;
}
}
  • lock 确保同一时间只有一个线程能进入代码块。

  • 性能较好,适合大多数情况。


🔹 方法 2:Interlocked(更轻量)

1
2
3
4
5
6
int counter = 0;

void Increment()
{
Interlocked.Increment(ref counter);
}
  • Interlocked 提供原子操作(IncrementDecrementAddCompareExchange)。

  • lock 更快,适合简单的数值操作。


🔹 方法 3:Monitor(比 lock 更底层)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
private object _lockObj = new object();
int counter = 0;

void Increment()
{
Monitor.Enter(_lockObj);
try
{
counter++;
}
finally
{
Monitor.Exit(_lockObj);
}
}
  • lock 其实就是 Monitor 的语法糖。

  • 如果需要更复杂的控制(Wait/Pulse),可以直接用 Monitor


🔹 方法 4:ReaderWriterLockSlim(读多写少场景)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
ReaderWriterLockSlim rwLock = new ReaderWriterLockSlim();
List<int> list = new List<int>();

void AddItem(int item)
{
rwLock.EnterWriteLock();
try
{
list.Add(item);
}
finally
{
rwLock.ExitWriteLock();
}
}

int GetItem(int index)
{
rwLock.EnterReadLock();
try
{
return list[index];
}
finally
{
rwLock.ExitReadLock();
}
}
  • 多个线程可同时读,但写时必须独占。

  • 比单纯的 lock 更高效(在读操作远多于写操作的情况下)。


CSharp多线程之RaceCondition
https://lshgame.com/2025/09/27/CSharp_Multithreading_RaceCondition/
作者
SuHang
发布于
2025年9月27日
许可协议