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
相关产品推荐
相关产品推荐

