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

C#事件调用的异步匿名方法中途终止未执行后续代码问题

问题现象

事件调用的异步匿名方法执行到中途终止,近半数代码未得到执行,调试过程中标注位置的代码始终无法执行,任务在调用Blockchain.CreateBlock方法处提前终止。

相关业务代码

事件触发时调用的方法

private void OnBlockGenerationTime()
{
    Task.Run(async () =>
    {
        await Task.Delay(10);

        if (!ChainMutex.WaitOne(3000))
            throw new TimeoutException("Cannot access to blockchain, mutex was held too long.");

        var winnerTrx = PoS.GetWinner(Blockchain.LatestBlock.ID);

        ChainMutex.ReleaseMutex();

        if (winnerTrx == null)
            return;

        var winner = PoolsSet.TransactionPool.GetTxOut(winnerTrx.Trx.Inputs[0]).Address;

        if (winner == CurrentAccount.GetAddress())
        {
            var (block, reward) = Blockchain.CreateBlock(CurrentAccount.GetPrivateKey()); // 任务在此处终止

            CommonLog.Log(LogElementType.OK, $"Our stake won. A new block with ID {block.ID} was created. {reward} AWT were collected.");

            P2P.BroadcastLatestBlock();
        }
    });
}

负责循环触发事件的无限循环任务方法

private void RunNewBlockListening()
{
    Task.Run(async () =>
    {
        while (true)
        {
            while (GetRemainingToNewBlock() != 0) { await Task.Delay(100); }

            OnNewBlockGeneration?.Invoke();

            await Task.Delay(1000);
        }
    });
}

我知晓异步方法的执行机制与同步方法存在差异,但调试过程中标注位置的代码始终无法执行,任务在调用Blockchain.CreateBlock方法处提前终止。

问题根因与修复方案

  • 最核心的问题是当前使用了无等待的火并(fire-and-forget)Task写法,Task内部抛出的所有异常默认会被线程池吞噬,不会触发上层未处理异常逻辑。如果调试时没开启“异常引发即中断”的配置,只会看到任务执行到某一行就莫名终止,看不到具体报错。优先打开调试器的CLR异常全中断开关,复现问题后就能直接看到CreateBlock内部抛出的具体错误,这是定位问题最快的方式。
  • 互斥锁释放逻辑存在致命缺陷:当前代码里只要ChainMutex.WaitOne获取锁成功后、ChainMutex.ReleaseMutex()执行前的任何一行代码抛出异常,锁就永远不会被释放,后续所有尝试获取该锁的逻辑都会等待3秒后抛出超时异常,直接终止任务。锁的释放必须放在try/finally块中,保证无论代码正常执行还是抛出异常都能正确释放锁。
  • Task.Run调度的代码运行在线程池工作线程上,如果Blockchain.CreateBlock方法内部存在依赖特定同步上下文的逻辑(比如访问UI控件、依赖线程静态变量、要求和其他逻辑跑在同一指定线程),在线程池线程上执行会直接抛出异常。

修正后参考写法

private void OnBlockGenerationTime()
{
    // 丢弃Task时必须加全局异常捕获,避免异常被线程池静默吞掉
    _ = Task.Run(async () =>
    {
        await Task.Delay(10);
        bool mutexAcquired = false;
        try
        {
            if (!ChainMutex.WaitOne(3000))
                throw new TimeoutException("Cannot access to blockchain, mutex was held too long.");
            mutexAcquired = true;

            var winnerTrx = PoS.GetWinner(Blockchain.LatestBlock.ID);
            if (winnerTrx == null)
                return;

            var winner = PoolsSet.TransactionPool.GetTxOut(winnerTrx.Trx.Inputs[0]).Address;
            if (winner != CurrentAccount.GetAddress())
                return;

            var (block, reward) = Blockchain.CreateBlock(CurrentAccount.GetPrivateKey());
            CommonLog.Log(LogElementType.OK, $"Our stake won. A new block with ID {block.ID} was created. {reward} AWT were collected.");
            P2P.BroadcastLatestBlock();
        }
        catch (Exception ex)
        {
            // 所有异常先落日志,避免无感知失败
            CommonLog.Log(LogElementType.Error, $"Block generation failed: {ex}");
        }
        finally
        {
            // 保证锁一定被释放
            if (mutexAcquired)
                ChainMutex.ReleaseMutex();
        }
    });
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.29 11:57:20