WinUI3 C#:主窗口关闭后如何保持后台线程(本地服务器)运行?
在WinUI3中实现主UI关闭后保持服务器线程运行的解决方案
问题核心在于:WinUI3应用默认会在最后一个窗口关闭时终止整个进程,无论线程是后台还是前台都会被强制终止。要实现需求需要两步关键操作:阻止窗口关闭触发进程退出,让服务器线程成为前台线程维持进程活跃。
步骤1:修改应用退出行为,阻止窗口关闭终止进程
在App.xaml.cs中配置应用仅在显式调用退出时才终止,同时拦截主窗口的关闭事件改为隐藏窗口:
using Microsoft.UI.Xaml; namespace YourAppNamespace; public partial class App : Application { private Window? _mainWindow; public App() { InitializeComponent(); // 设置仅显式调用Exit()才会终止应用 this.SetExitMode(ApplicationExitMode.Explicit); } protected override void OnLaunched(LaunchActivatedEventArgs args) { _mainWindow = new MainWindow(); _mainWindow.Closed += MainWindow_Closed; _mainWindow.Activate(); } private void MainWindow_Closed(object sender, WindowEventArgs e) { // 阻止窗口关闭触发应用退出,改为隐藏窗口 e.Handled = true; _mainWindow?.Hide(); } }
步骤2:创建前台服务器线程并实现优雅启停
将服务器线程设置为前台线程(IsBackground = false),这样只要该线程在运行,进程就会保持活跃。同时添加取消令牌实现优雅终止:
using System.Net; using System.Net.Sockets; using System.Threading; using System.Threading.Tasks; // 在MainWindow或对应业务类中实现 private Thread? _serverThread; private CancellationTokenSource? _serverCts; // 启动服务器的方法 public void StartLocalServer() { _serverCts = new CancellationTokenSource(); _serverThread = new Thread(() => RunServerLoop(_serverCts.Token)) { IsBackground = false, // 前台线程,维持进程存活 Name = "LocalSocketServer" }; _serverThread.Start(); } private void RunServerLoop(CancellationToken cancellationToken) { var listener = new TcpListener(IPAddress.Loopback, 8080); listener.Start(); try { while (!cancellationToken.IsCancellationRequested) { // 用Pending()+睡眠避免AcceptTcpClient阻塞导致无法终止 if (listener.Pending()) { var client = listener.AcceptTcpClient(); // 用线程池处理客户端连接,避免阻塞服务器主线程 ThreadPool.QueueUserWorkItem(HandleClientConnection, client); } else { Thread.Sleep(100); } } } catch (SocketException ex) { // 排除主动终止时的中断异常 if (ex.SocketErrorCode != SocketError.Interrupted && !cancellationToken.IsCancellationRequested) { // 这里添加异常日志或处理逻辑 } } finally { listener.Stop(); } } private void HandleClientConnection(object? clientObj) { if (clientObj is not TcpClient client) return; // 这里实现具体的Socket通信逻辑 // 示例:读取客户端数据并返回 using var stream = client.GetStream(); var buffer = new byte[1024]; var bytesRead = stream.Read(buffer, 0, buffer.Length); // ... 处理数据逻辑 client.Close(); } // 主动退出应用时调用(比如系统托盘的退出按钮) public void ShutdownServerAndExit() { // 取消服务器循环 _serverCts?.Cancel(); // 等待线程优雅终止(最多1秒) _serverThread?.Join(1000); // 显式终止应用进程 Application.Current.Exit(); }
关键注意事项
- 必须提供手动退出入口:因为主窗口关闭只是隐藏,进程仍在后台运行,需要给用户提供退出方式(比如添加系统托盘图标,或在隐藏窗口的右键菜单添加退出选项),调用
ShutdownServerAndExit()彻底终止进程。 - 优雅终止线程:使用
CancellationToken控制服务器循环,避免强制终止导致Socket资源泄漏。 - 客户端连接处理:用线程池处理每个客户端连接,防止服务器主线程被阻塞。
内容的提问来源于stack exchange,提问作者Abdullah Omer
相关产品推荐
相关产品推荐

