You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何用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)**中,确保多进程能访问同一块内存区域:

  1. 使用MemoryMappedFile.CreateOrOpen创建或打开共享内存。
  2. 通过MemoryMappedViewAccessor读写共享内存中的变量。
  3. 锁的核心逻辑保持不变,仅需将变量的读写操作替换为内存映射视图访问器的方法。

简化的跨进程共享计数器示例:

// 进程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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.09 07:25:22