如何用Interlocked.CompareExchange实现进程间锁?代码问题排查
代码错误排查与修复
原代码的核心错误
- 锁释放逻辑致命错误:释放锁时执行
Volatile.Write(ref _lock, current)会导致多线程同时进入临界区。假设初始_lock为0,线程A获取锁后将其改为自身id,执行完临界区又把_lock改回0;此时所有等待的线程都会尝试用自己的id替换0,多个线程会同时通过CompareExchange校验,最终并发修改_counter,导致计数结果混乱。 - 锁状态设计混乱:用递增的
_id作为锁持有标识,没有固定的“无锁”状态,锁的释放与获取逻辑无法形成可靠同步。
正确的自旋锁实现(基于Interlocked.CompareExchange)
要实现可靠的自旋锁,需固定一个“无锁”状态(比如0),线程通过CompareExchange尝试将锁从无锁状态改为持有状态(比如1),成功则进入临界区;释放锁时必须将状态改回无锁状态,确保同一时间只有一个线程能进入临界区。
修复后的代码:
var counter = new ConcurrentCounter(); var count = counter.Count(); Console.WriteLine(count); public class ConcurrentCounter { // 0 = 无锁,1 = 已锁定 private int _lock; private int _counter; public int Count() { var threads = Enumerable.Range(0, 10).Select(_ => new Thread(Worker)).ToArray(); foreach (var thread in threads) thread.Start(); foreach (var thread in threads) thread.Join(); return _counter; } private void Worker() { for (var i = 0; i < 1000000; i++) { // 自旋等待获取锁:尝试将_lock从0改为1,成功则拿到锁 while (Interlocked.CompareExchange(ref _lock, 1, 0) != 0) { // 调用SpinWait减少CPU空转消耗 Thread.SpinWait(10); } // 临界区操作 _counter++; // 释放锁,用VolatileWrite保证跨线程可见性 Volatile.Write(ref _lock, 0); } } }
进程间共享内存场景的额外注意事项
如果要用于跨进程同步,普通类字段无法满足需求,必须将_lock和_counter放在**内存映射文件(MemoryMappedFile)**中,确保多进程能访问同一块内存区域:
- 使用
MemoryMappedFile.CreateOrOpen创建或打开共享内存。 - 通过
MemoryMappedViewAccessor读写共享内存中的变量。 - 锁的核心逻辑保持不变,仅需将变量的读写操作替换为内存映射视图访问器的方法。
简化的跨进程共享计数器示例:
// 进程1:创建共享内存并执行计数 using var mmf = MemoryMappedFile.CreateOrOpen("SharedCounter", 16); using var accessor = mmf.CreateViewAccessor(); // 初始化锁为0,计数器为0 accessor.Write(0, 0); accessor.Write(4, 0); // 启动线程执行计数逻辑(读写操作改用accessor.ReadInt32/Write) // ... // 进程2:读取共享内存中的计数器值 using var mmf = MemoryMappedFile.OpenExisting("SharedCounter"); using var accessor = mmf.CreateViewAccessor(); int counter = accessor.ReadInt32(4); Console.WriteLine(counter);
内容的提问来源于stack exchange,提问作者user1576055
相关产品推荐
相关产品推荐

