为啥 .NET 通用主机在与 WinUI3 一起使用时不会停止?
Posted
技术标签:
【中文标题】为啥 .NET 通用主机在与 WinUI3 一起使用时不会停止?【英文标题】:Why doesn't the .NET Generic Host stop when used with WinUI3?为什么 .NET 通用主机在与 WinUI3 一起使用时不会停止? 【发布时间】:2021-08-21 19:22:39 【问题描述】:我正在使用 .NET 5 编写一个 WinUI3 (Project Reunion 0.5) 应用程序,并希望使用 .NET 通用主机。我正在使用带有自定义 IHostedService
的默认主机:
public App()
_host = Host.CreateDefaultBuilder()
.ConfigureServices((context, services) =>
services.AddHostedService<MyHostedService>();
).Build();
InitializeComponent();
托管服务在StopAsync
中执行一些异步操作。出于演示目的,假设它延迟 1 秒(此代码仍然会产生问题):
public override async Task StopAsync(CancellationToken cancellationToken)
await Task.Delay(1000);
我在OnLaunched
启动主机:
protected override async void OnLaunched(Microsoft.UI.Xaml.LaunchActivatedEventArgs args)
await _host.StartAsync();
m_window = new MainWindow();
m_window.Activate();
我让默认的ConsoleLifetime
实现在进程退出之前停止主机。
我的IHostedService.StopAsync
实现返回的Task
完成,但IHost.StopAsync
永远不会返回,并且进程挂起并在输出中显示此消息:
Microsoft.Hosting.Lifetime: Information: Application is shutting down...
Microsoft.Hosting.Lifetime: Information: Waiting for the host to be disposed. Ensure all 'IHost' instances are wrapped in 'using' blocks.
如果我使用调试器单步执行,有时IHost.StopAsync
方法会超时并抛出异常。这永远不会在调试器之外发生。当MainWindow
关闭时,我尝试过显式停止和处理主机,但没有任何区别。
我认为DispatcherQueueSynchronizationContext
可能在主机停止并且任务未得到服务之前被关闭,但DispatcherQueue.ShutdownStarting
事件从未被触发。
还有其他想法吗?
【问题讨论】:
您是否在使用具有确定性垃圾收集功能的编程环境(如 C++)时遇到同样的问题?App()
在哪里定义?您使用的是 WPF、UWP XAML 还是其他一些 XAML 框架?
你真的不应该在App.xaml
的构造函数中设置你的IHost
,因为它隐藏了很多设置发生在你程序的其他地方的事实,这可能是导致问题的原因你有。相反,您需要定义自己的Main
并设置IHost
before WPF、UWP、Jupiter、whatever-Microsoft-is-calling-their-latest-XAML-environment-this -season 开始。
@IInspectable C++ 不是“确定性垃圾收集”语言:real C++ 中根本没有垃圾收集。即使是这样,C++ 也不能用于托管 IHost
,因为这需要 CLR。 (并且编译 C++/CLI 不会神奇地让您在“真正的”C++ 中使用 CLR 类型)。
@dai 你确定你了解垃圾收集是什么?原因everybody thinks about garbage collection the wrong way.
【参考方案1】:
我听取了 cmets 的 @Dai 的建议,并研究了在单独的线程上运行 WinUI 并在主线程上运行主机。
我创建了一个IHostedService
来管理 WinUI 应用程序:
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Options;
using Microsoft.System;
using Microsoft.UI.Xaml;
using System;
using System.Threading;
using System.Threading.Tasks;
namespace MyApp.Hosting
public class WinUIHostedService<TApplication> : IHostedService, IDisposable
where TApplication : Application, new()
private readonly IHostApplicationLifetime HostApplicationLifetime;
private readonly IServiceProvider ServiceProvider;
public WinUIHostedService(
IHostApplicationLifetime hostApplicationLifetime,
IServiceProvider serviceProvider)
HostApplicationLifetime = hostApplicationLifetime;
ServiceProvider = serviceProvider;
public void Dispose()
public Task StartAsync(CancellationToken cancellationToken)
var thread = new Thread(Main);
thread.SetApartmentState(ApartmentState.STA);
thread.Start();
return Task.CompletedTask;
public Task StopAsync(CancellationToken cancellationToken)
return Task.CompletedTask;
private void Main()
WinRT.ComWrappersSupport.InitializeComWrappers();
Application.Start((p) =>
var context = new DispatcherQueueSynchronizationContext(DispatcherQueue.GetForCurrentThread());
SynchronizationContext.SetSynchronizationContext(context);
new TApplication();
);
HostApplicationLifetime.StopApplication();
我在构建设置中定义了DISABLE_XAML_GENERATED_MAIN
,并添加了我自己的Main
:
public class Program
public static void Main(string[] args)
Host.CreateDefaultBuilder()
.ConfigureServices(services =>
services.AddHostedService<WinUIHostedService<App>>();
)
.Build().Run();
瞧!即使IHostedService.StopAsync
运行异步代码,WinUI 应用程序仍然可以正常运行并且主机在主窗口关闭时完全停止。
请注意,此代码只是第一个起作用的代码。它可能会被改进,我不完全理解通用主机生命周期语义。
【讨论】:
感谢您这样做。我真的很喜欢使用托管管道,遵循这种模式非常有意义。希望有一些官方指南或扩展。由于 Maui 正在使用 Hosting/StartUp 模式做类似的事情以上是关于为啥 .NET 通用主机在与 WinUI3 一起使用时不会停止?的主要内容,如果未能解决你的问题,请参考以下文章
为啥 LLVM 的泄漏消毒剂在与其他启用的消毒剂一起使用时不起作用
WinUI 3.0 - 为啥 UWP 项目要求 MS Edge for Business 用于 WebView2