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

控制台应用未等待异步延迟?服务端关机异步延迟失效排查

解决服务端异步关机时程序直接关闭的问题

看起来你遇到的核心问题是异步方法没有被等待,导致主线程提前退出,进而Stop里的延迟和Dispose逻辑都没机会执行。咱们一步步拆解问题并修复:

问题根源

你调用Server?.Stop(rebootAfterClose)的时候,因为Stop是async Task方法,但你没有用await等待它完成。主线程会直接跳过这个异步操作,继续执行后续代码(甚至直接退出进程),导致await Task.Delay(5000)还没执行完,程序就已经关闭了。

另外Dispose里的Environment.Exit(0)会强制终止进程,如果这个调用时机不对,也会打断正常的清理流程。

修复步骤

1. 等待异步Stop方法完成

首先要修改调用Stop的代码,确保主线程会等待Stop里的所有逻辑执行完毕。

如果你用的是C# 7.1+(支持异步Main方法)

把你的入口Main方法改成async Task类型,然后用await调用Stop:

static async Task Main(string[] args)
{
    // ... 你的其他初始化代码 ...

    var rebootAfterClose = false;
    while (true) {
        var input = Console.ReadKey();
        if (input.Key == ConsoleKey.C && input.Modifiers == ConsoleModifiers.Control) {
            break;
        }
        if (input.Key != ConsoleKey.R || input.Modifiers != ConsoleModifiers.Control) {
            continue;
        }
        rebootAfterClose = true;
        break;
    }

    // 等待Stop方法执行完成
    if (Server != null)
    {
        await Server.Stop(rebootAfterClose);
    }
}

如果是旧版本C#(不支持异步Main)

可以用.GetAwaiter().GetResult()来同步等待异步任务完成:

// 原来的调用处改成
Server?.Stop(rebootAfterClose).GetAwaiter().GetResult();

2. 调整Stop和Dispose的逻辑

接下来优化关机流程,避免强制退出打断清理,同时确保重启逻辑能正常执行:

修改后的Stop方法:

public async Task Stop(bool restart) {
    // 发送关机提示给所有玩家
    var shutdownMessage = PlusEnvironment.GetLanguageManager().TryGetValue("server.shutdown.message");
    var shutdownPacket = new BroadcastMessageAlertComposer(shutdownMessage);
    Program.Server.BaseHandler.PlayerHandler.SendPacketToPlayers(shutdownPacket);

    // 等待5秒,让玩家看到关机提示
    await Task.Delay(5000);

    // 执行资源清理
    Dispose();

    // 如果需要重启,在这里启动新进程(放在Dispose之后,确保旧进程清理完成)
    if (restart)
    {
        var currentProcess = System.Diagnostics.Process.GetCurrentProcess();
        System.Diagnostics.Process.Start(currentProcess.MainModule.FileName);
    }
}

修改后的Dispose方法(去掉强制退出,让进程自然结束):

public void Dispose() {
    if (Disposing) {
        return;
    }
    Disposing = true;
    Console.Clear();
    Console.WriteLine();
    Console.Title = "Server is shutting down...";
    Logger.Warn("Server is shutting down...");

    // 依次清理各个组件
    BaseHandler.PlayerHandler.Dispose();
    ConsoleUpdater.Dispose();
    SocketHandler.Dispose();
    PlusEnvironment.OnShutDown();

    // 等待2秒,确保日志和清理操作完成
    Thread.Sleep(2000);

    // 移除强制退出,让进程在Main方法完成后自然关闭
    // Environment.Exit(0);
}

额外说明

  • 不要在异步方法里混用Thread.Sleep和Task.Delay:Thread.Sleep会阻塞当前线程,而Task.Delay是异步等待,不会冻结游戏主线程,这部分你已经做对了。
  • 重启逻辑要放在Dispose之后:确保旧进程的所有资源都清理完成后,再启动新进程,避免资源占用冲突。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.15 04:07:26