在现代软件开发中,多线程编程是提升应用性能的重要手段。然而,多线程环境引入了一系列复杂的问题,若处理不当,可能导致程序崩溃、数据不一致或性能下降。本文将结合实战经验,深入探讨.NET 多线程编程中的常见陷阱,并提供相应的解决方案和性能优化建议。
一、线程安全问题:共享资源的竞态条件
1. 经典案例:计数器递增的竞态条件
问题代码:
class Counter
{
private int count = 0;
public void Increment()
{
count++; // 非原子操作,存在竞态条件
}
public int Value => count;
}
问题分析:
count++
实际上是三个操作:读取值、加 1、写入值- 多线程环境下可能导致值被覆盖,例如:
Thread 1: 读取 count = 0 Thread 2: 读取 count = 0 Thread 1: 计算 0+1=1,写入 count = 1 Thread 2: 计算 0+1=1,写入 count = 1 (覆盖了Thread 1的结果)
解决方案:
class ThreadSafeCounter
{
private int count = 0;
public void Increment()
{
Interlocked.Increment(ref count); // 原子操作
}
public int Value => count;
}
2. 复合操作的原子性
问题场景:检查然后执行(Check-Then-Act)操作
if (cache[key] == null) // 检查
{
cache[key] = CreateItem(key); // 执行
}
问题分析:
- 两个线程可能同时通过检查,导致重复创建对象或数据覆盖
解决方案:使用锁机制
private readonly object _lock = new object();
public void AddItem(string key, object value)
{
lock (_lock) // 确保同一时间只有一