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

C#可变数量多对象锁定及异常时锁自动释放方案咨询

核心实现思路

首先必须遵守固定顺序获取锁的原则,所有场景下都按下标从小到大的顺序申请锁,从根源避免死锁。再通过try-finally结构保证无论是否抛出异常,已获取的锁都会被正确释放。

方案1:同进程内优先用Monitor(对应C# lock 关键字底层实现)

同进程内多线程同步时,Monitor比Mutex轻量得多,性能开销更低,是首选方案。

实现代码

class A
{
    public List<int> References { get; } = new List<int>();
}

// 初始化部分
int N = 100; // 替换为实际数组长度
A[] a_array = new A[N];
// 注意必须初始化每个锁对象,不能留空
object[] locks = Enumerable.Range(0, N).Select(_ => new object()).ToArray();

/// <summary>
/// 安全操作指定下标的A实例
/// </summary>
/// <param name="targetIndex">要操作的实例下标</param>
/// <param name="operation">要执行的读写操作逻辑</param>
public void SafeOperateA(int targetIndex, Action operation)
{
    if (targetIndex < 0 || targetIndex >= N)
        throw new ArgumentOutOfRangeException(nameof(targetIndex));

    // 1. 收集所有需要加锁的下标:目标实例本身 + 所有引用的实例,去重避免重复加锁
    var requireLockIndexes = new HashSet<int> { targetIndex };
    foreach (var refer in a_array[targetIndex].References)
    {
        if (refer <0 || refer >= N) 
            throw new InvalidOperationException($"引用下标{refer}越界");
        requireLockIndexes.Add(refer);
    }

    // 2. 按下标升序排序,保证所有线程都按相同顺序拿锁,彻底避免死锁
    var sortedIndexes = requireLockIndexes.OrderBy(i => i).ToList();
    // 记录已经成功获取的锁,用于异常时释放
    List<object> acquiredLocks = new List<object>(sortedIndexes.Count);

    try
    {
        // 3. 按顺序获取所有锁
        foreach (var idx in sortedIndexes)
        {
            var lockObj = locks[idx];
            Monitor.Enter(lockObj);
            acquiredLocks.Add(lockObj);
        }

        // 4. 所有锁获取成功,执行业务逻辑
        operation.Invoke();
    }
    finally
    {
        // 5. 释放所有已获取的锁
        for (int i = acquiredLocks.Count -1; i >=0; i--)
        {
            Monitor.Exit(acquiredLocks[i]);
        }
    }
}

使用示例

// 要操作下标为5的A实例,读取它和它引用的所有实例的内容做计算
SafeOperateA(5, () =>
{
    var targetA = a_array[5];
    // 这里可以安全读写targetA以及它References里的所有实例
    foreach (var referIdx in targetA.References)
    {
        var referA = a_array[referIdx];
        // 编写你的业务逻辑
    }
});

方案2:跨进程同步用Mutex

如果需要跨进程锁定这组资源,才需要用到Mutex,同样遵守按顺序拿锁+异常释放的逻辑:

实现代码

// 初始化Mutex数组,注意Mutex名称要全局唯一避免和其他进程冲突
Mutex[] mutexes = Enumerable.Range(0, N)
    .Select(i => new Mutex(false, $"Global_AInstanceLock_{i}"))
    .ToArray();

public void CrossProcessSafeOperateA(int targetIndex, Action operation)
{
    // 收集去重、排序下标逻辑和方案1完全一致
    var requireLockIndexes = new HashSet<int> { targetIndex };
    foreach (var refer in a_array[targetIndex].References)
        requireLockIndexes.Add(refer);
    var sortedIndexes = requireLockIndexes.OrderBy(i => i).ToList();
    List<Mutex> acquiredMutexes = new List<Mutex>(sortedIndexes.Count);

    try
    {
        foreach (var idx in sortedIndexes)
        {
            var mutex = mutexes[idx];
            // 阻塞等待获取锁,可根据需要传入超时时间避免永久卡住
            mutex.WaitOne();
            acquiredMutexes.Add(mutex);
        }

        operation.Invoke();
    }
    finally
    {
        // 释放所有已获取的Mutex
        foreach (var mutex in acquiredMutexes)
        {
            mutex.ReleaseMutex();
        }
    }
}

注意:Mutex属于内核对象,性能远低于Monitor,仅跨进程场景使用;且Windows下全局Mutex需要管理员权限才能创建,建议根据实际场景调整命名前缀。


内容的提问来源于stack exchange,提问作者user107511

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.24 02:06:06