控制台应用未等待异步延迟?服务端关机异步延迟失效排查
解决服务端异步关机时程序直接关闭的问题
看起来你遇到的核心问题是异步方法没有被等待,导致主线程提前退出,进而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
相关产品推荐
相关产品推荐

