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

两组操作同步方案咨询:组内共享、组间互斥的同步机制

这是个非常典型的双组互斥共享场景,刚好可以通过组合基础同步原语来实现,或者封装成自定义锁类来复用。下面我给你两种实用的实现思路,附代码示例:

核心逻辑梳理

我们需要的是:

  • A组内任意数量操作可并发执行
  • B组内任意数量操作可并发执行
  • A、B组操作绝对互斥,同一时间只能有一组在运行

本质上这是把ReaderWriterLockSlim的"读"权限拆成了两个独立的共享组,组间互斥,组内共享。

方案一:自定义双组同步锁类(推荐复用)

这个方案封装成独立类,代码更整洁,复用性强。这里用C#的Monitor来实现条件等待(类似原生的条件变量):

public class GroupedMutex
{
    private readonly object _syncLock = new object();
    private int _activeGroup = 0; // 0=无活跃组,1=A组,2=B组
    private int _aActiveCount = 0;
    private int _bActiveCount = 0;

    // 进入A组
    public void EnterGroupA()
    {
        lock (_syncLock)
        {
            // 等待B组所有操作完全退出
            while (_activeGroup == 2)
            {
                Monitor.Wait(_syncLock);
            }
            // 标记当前活跃组为A,增加A组计数
            _activeGroup = 1;
            _aActiveCount++;
        }
    }

    // 退出A组
    public void ExitGroupA()
    {
        lock (_syncLock)
        {
            if (--_aActiveCount == 0)
            {
                // A组最后一个操作退出,重置状态并通知等待的B组
                _activeGroup = 0;
                Monitor.PulseAll(_syncLock);
            }
        }
    }

    // 进入B组
    public void EnterGroupB()
    {
        lock (_syncLock)
        {
            // 等待A组所有操作完全退出
            while (_activeGroup == 1)
            {
                Monitor.Wait(_syncLock);
            }
            _activeGroup = 2;
            _bActiveCount++;
        }
    }

    // 退出B组
    public void ExitGroupB()
    {
        lock (_syncLock)
        {
            if (--_bActiveCount == 0)
            {
                _activeGroup = 0;
                Monitor.PulseAll(_syncLock);
            }
        }
    }

    // 异步版本(适配异步操作场景)
    public async Task EnterGroupAAsync()
    {
        // 用Task.Run包装同步锁,避免阻塞异步上下文
        await Task.Run(EnterGroupA);
    }

    public async Task ExitGroupAAsync()
    {
        await Task.Run(ExitGroupA);
    }

    public async Task EnterGroupBAsync()
    {
        await Task.Run(EnterGroupB);
    }

    public async Task ExitGroupBAsync()
    {
        await Task.Run(ExitGroupB);
    }
}

使用示例

var groupLock = new GroupedMutex();

// A组操作
void RunGroupATask()
{
    groupLock.EnterGroupA();
    try
    {
        Console.WriteLine($"A组操作执行中,线程ID: {Thread.CurrentThread.ManagedThreadId}");
        // 模拟业务逻辑
        Thread.Sleep(1500);
    }
    finally
    {
        groupLock.ExitGroupA();
        Console.WriteLine($"A组操作完成,线程ID: {Thread.CurrentThread.ManagedThreadId}");
    }
}

// B组操作
void RunGroupBTask()
{
    groupLock.EnterGroupB();
    try
    {
        Console.WriteLine($"B组操作执行中,线程ID: {Thread.CurrentThread.ManagedThreadId}");
        Thread.Sleep(1000);
    }
    finally
    {
        groupLock.ExitGroupB();
        Console.WriteLine($"B组操作完成,线程ID: {Thread.CurrentThread.ManagedThreadId}");
    }
}

// 测试:启动3个A组线程,2个B组线程
for (int i = 0; i < 3; i++)
{
    new Thread(RunGroupATask).Start();
}
Thread.Sleep(500); // 让A组先启动
for (int i = 0; i < 2; i++)
{
    new Thread(RunGroupBTask).Start();
}

方案二:轻量实现(无需自定义类)

如果只是临时场景,不想封装类,可以用SemaphoreSlim加全局锁快速实现:

// 全局同步变量
private readonly object _crossGroupMutex = new object();
private readonly SemaphoreSlim _aSemaphore = new SemaphoreSlim(int.MaxValue);
private readonly SemaphoreSlim _bSemaphore = new SemaphoreSlim(int.MaxValue);
private int _currentActiveGroup = 0; // 0=无,1=A,2=B

// 执行A组操作的包装方法
async Task ExecuteGroupA(Func<Task> action)
{
    await _aSemaphore.WaitAsync();
    try
    {
        lock (_crossGroupMutex)
        {
            // 等待B组完全退出
            while (_currentActiveGroup == 2)
                Monitor.Wait(_crossGroupMutex);
            _currentActiveGroup = 1;
        }
        // 执行A组业务逻辑
        await action();
    }
    finally
    {
        lock (_crossGroupMutex)
        {
            _aSemaphore.Release();
            // 检查是否是最后一个A组操作,是的话重置状态并通知B组
            if (_aSemaphore.CurrentCount == int.MaxValue)
            {
                _currentActiveGroup = 0;
                Monitor.PulseAll(_crossGroupMutex);
            }
        }
    }
}

// 执行B组操作的包装方法
async Task ExecuteGroupB(Func<Task> action)
{
    await _bSemaphore.WaitAsync();
    try
    {
        lock (_crossGroupMutex)
        {
            while (_currentActiveGroup == 1)
                Monitor.Wait(_crossGroupMutex);
            _currentActiveGroup = 2;
        }
        await action();
    }
    finally
    {
        lock (_crossGroupMutex)
        {
            _bSemaphore.Release();
            if (_bSemaphore.CurrentCount == int.MaxValue)
            {
                _currentActiveGroup = 0;
                Monitor.PulseAll(_crossGroupMutex);
            }
        }
    }
}

关键注意事项

  • 无论哪种方案,必须在finally块中释放锁/信号量,避免因异常导致死锁
  • 异步场景优先使用带Async的方法,避免阻塞线程池
  • 如果需要限制组内并发数(比如最多5个A组操作同时执行),只需要把SemaphoreSlim的初始计数改成对应数字即可

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.27 03:41:44