【C#】`Interlocked` vs `lock`
Interlocked
和 lock
是 C# 中常用的两种线程同步机制,但它们用途不同、性能差距大。下面我们来详细对比一下:
🆚 Interlocked
vs lock
对比一览
项目 | Interlocked | lock |
---|---|---|
🧠 本质 | 原子操作指令(CPU级别) | 加锁机制(线程阻塞) |
🚀 性能 | 极高(轻量,适合频繁调用) | 相对较低(阻塞线程) |
✅ 原子性 | ✅ 支持 | ✅ 支持 |
✅ 可见性 | ✅ 有效 | ✅ 有效 |
✅ 线程安全 | ✅ 是 | ✅ 是 |
⚠️ 可操作范围 | 仅限数值或引用操作 | 可以包住任何代码块 |
🛠 使用复杂度 | 简单,但受限 | 灵活,控制力强 |
🚫 可重入性 | 无需考虑 | 需要注意死锁、递归 |
🔄 阻塞 | 不阻塞线程 | 会阻塞其他线程直到释放锁 |
📌 什么时候用 Interlocked
适合非常简单的并发修改操作,如:
- 计数器递增
- 设置引用对象
- 比较交换值(CAS)
🔧 示例:线程安全地计数
private int counter = 0;
public void Increment()
{
Interlocked.Increment(ref counter);
}
这是线程安全的,因为 Interlocked.Increment
是原子操作,在底层是汇编指令,不需要锁。
📌 什么时候用 lock
适合多个操作需要作为一个整体执行,或者操作逻辑较复杂,比如:
- 修改多个字段
- 需要包含条件判断
- 涉及复杂流程逻辑
🔧 示例:线程安全地修改共享对象
private object _lockObj = new object();
private List<string> list = new List<string>();
public void AddItem(string item)
{
lock (_lockObj)
{
if (!list.Contains(item))
{
list.Add(item);
}
}
}
✅ 举个实际例子对比:
比如你要实现一个线程安全的 ID 生成器:
用 Interlocked
:
private int _nextId = 0;
public int GetNextId()
{
return Interlocked.Increment(ref _nextId);
}
如果用 lock
也可以:
private object _lock = new object();
private int _nextId = 0;
public int GetNextId()
{
lock (_lock)
{
return ++_nextId;
}
}
效果一样,但 Interlocked
的性能更高,尤其在高并发下。
🧠 总结一句话:
如果你只是修改一个变量,优先用
Interlocked
,性能更高;
如果你要操作多个变量或执行复杂逻辑,就用lock
,更通用更灵活。
你这个相机采集系统如果涉及采图计数、图像编号、控制状态这些,可以大量使用 Interlocked
;如果是控制图像队列、处理流程,就可能需要 lock
。
Interlocked其他操作
🎯 Interlocked
不只是递增,它支持多种原子操作,只要是对整数或引用的线程安全操作,Interlocked
几乎都有对应的方法。
下面是它的几种常用操作:
✅ 常用 Interlocked
操作一览
方法名 | 说明 | 示例 |
---|---|---|
Increment(ref int) | 原子递增 +1 | Interlocked.Increment(ref counter); |
Decrement(ref int) | 原子递减 -1 | Interlocked.Decrement(ref counter); |
Add(ref int, value) | 原子加任意数 | Interlocked.Add(ref total, 5); |
Exchange(ref T, value) | 原子替换值(赋新值,返回旧值) | Interlocked.Exchange(ref flag, 1); |
CompareExchange(ref T, newValue, expected) | 原子比较并替换(CAS) | Interlocked.CompareExchange(ref flag, 1, 0); |
🔍 举几个实用例子
🔢 原子加减任意值
int total = 0;
Interlocked.Add(ref total, 10); // 加 10
Interlocked.Add(ref total, -3); // 减 3
🔄 原子交换值(Exchange)
适用于原子性“状态切换”:
int isProcessing = 0;
// 尝试设置为“处理中”
if (Interlocked.Exchange(ref isProcessing, 1) == 0)
{
// 成功设置为处理状态
DoWork();
// 处理完成,重置
Interlocked.Exchange(ref isProcessing, 0);
}
💣 原子“只有一个线程能进来”:CompareExchange(CAS)
int started = 0;
if (Interlocked.CompareExchange(ref started, 1, 0) == 0)
{
// 只有第一次会进来
Console.WriteLine("Only one thread can enter here.");
}
解释:
- 如果当前值是 0,就设置为 1,表示“我抢到了锁”
- 返回原值(如果不是 0 就说明别人先抢到了)
🧠 总结一句话:
Interlocked
是一个线程安全的整数和引用操作工具类,不仅能递增递减,还能实现原子加法、值替换、CAS 等,高性能、无锁,非常适合并发场景!
如果你在做一些并发计数器、控制标志位、实现轻量锁,Interlocked
是利器!