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

多线程TIC/TAC交替输出异常:Mutex同步问题求助

修复TIC-TAC线程同步异常并实现交替输出

错误原因分析

你遇到的System.ApplicationException(对象同步方法从非同步代码块调用),核心原因是Mutex的所有权规则:只有通过WaitOne()获取到Mutex所有权的线程,才能调用ReleaseMutex()释放它。你的代码存在两处违反规则的情况:

  1. 初始化mutexTIC时传入true,意味着创建Class1实例的主线程直接拥有了mutexTIC的所有权,但后续是TAC线程尝试调用mutexTIC.ReleaseMutex(),完全不具备所有权。
  2. 多个TIC/TAC线程交叉操作Mutex,导致所有权混乱,比如TIC线程释放mutexTAC,但mutexTAC可能被其他TAC线程持有,同样触发异常。

解决方案:用AutoResetEvent实现交替同步

AutoResetEvent天生适合这种"交替唤醒"的场景,它会在调用Set()后自动重置为非信号状态,刚好匹配TIC-TAC的执行逻辑:

public class Class1
{
    // 初始允许TIC执行,TAC线程等待
    private readonly AutoResetEvent _ticSignal = new AutoResetEvent(true);
    private readonly AutoResetEvent _tacSignal = new AutoResetEvent(false);
    // 记录已输出的TIC次数,控制总交替次数(3次)
    private int _ticExecutedCount;
    private readonly object _countLock = new object();

    public void TIC()
    {
        _ticSignal.WaitOne();
        
        lock (_countLock)
        {
            // 已完成3次TIC则直接唤醒TAC线程并退出
            if (_ticExecutedCount >= 3)
            {
                _tacSignal.Set();
                return;
            }
            Console.Write("TIC ");
            _ticExecutedCount++;
        }
        
        // 唤醒TAC线程执行
        _tacSignal.Set();
    }

    public void TAC()
    {
        _tacSignal.WaitOne();
        
        lock (_countLock)
        {
            // 无有效TIC输出则直接唤醒TIC线程并退出
            if (_ticExecutedCount == 0 || _ticExecutedCount > 3)
            {
                _ticSignal.Set();
                return;
            }
            Console.Write("TAC ");
        }
        
        // 唤醒下一个TIC线程执行
        _ticSignal.Set();
    }
}

另一种方案:用Monitor+状态标记实现

如果不想用Event类,也可以用.NET内置的Monitor结合状态变量实现线程同步,逻辑更直观:

public class Class1
{
    private readonly object _syncLock = new object();
    // 标记当前是否轮到TIC输出
    private bool _isTicTurn = true;
    // 记录已完成的TIC-TAC配对数
    private int _completedPairs;

    public void TIC()
    {
        lock (_syncLock)
        {
            // 等待轮到自己,或已完成3次配对则退出
            while (!_isTicTurn && _completedPairs < 3)
            {
                Monitor.Wait(_syncLock);
            }
            if (_completedPairs >= 3) return;
            
            Console.Write("TIC ");
            _isTicTurn = false;
            _completedPairs++;
            // 唤醒所有等待的线程,通知状态变更
            Monitor.PulseAll(_syncLock);
        }
    }

    public void TAC()
    {
        lock (_syncLock)
        {
            // 等待轮到自己,或已完成配对则退出
            while (_isTicTurn && _completedPairs < 3)
            {
                Monitor.Wait(_syncLock);
            }
            if (_completedPairs > 3) return;
            
            Console.Write("TAC ");
            _isTicTurn = true;
            Monitor.PulseAll(_syncLock);
        }
    }
}

验证效果

保持你原来的线程创建和启动代码不变,运行后会稳定输出:TIC TAC TIC TAC TIC TAC

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.07 20:39:52