AppHost.RunAsync()无限挂起问题排查求助
问题描述
运行一个通过Autofac配置了Windows后台服务、邮件服务、日志及DI对象的应用时,AppHost.RunAsync()出现无限挂起,无法返回控制权。此前代码运行正常,多次运行后才触发该问题。已确认服务注册无异常,即使仅保留日志、ProcessorOptions等基础服务,AppHost仍无法正常运行,求排查方向。
Program.cs
private static async Task Main(string[] args) { try { Directory.SetCurrentDirectory(AppDomain.CurrentDomain.BaseDirectory); LogManager.Configuration = new XmlLoggingConfiguration("NLog.config"); log = LogManager.GetCurrentClassLogger(); var builder = Host.CreateDefaultBuilder(args); AppHost = builder .UseServiceProviderFactory(ctx => { AutofacConfiguration.Configuration = ctx.Configuration; return new AutofacServiceProviderFactory(AutofacConfiguration.BuildContainer); }) .ConfigureServices((ctx, services) => { var svcOptions = ctx.Configuration.GetSection(ServiceEntryPointOptions.ConfigSectionName).Get<ServiceEntryPointOptions>(); WindowsBackgroundService.WindowsServiceKey = $"{svcOptions.ServiceName}${svcOptions.InstanceName}"; services.AddHostedService<WindowsBackgroundService>(); }) .UseWindowsService(options => { options.ServiceName = WindowsBackgroundService.WindowsServiceKey; }) .Build(); await AppHost.RunAsync(); } catch (Exception e) { } finally { AppHost?.Dispose(); } }
AutofacConfiguration.cs
public partial class AutofacConfiguration { public static IConfiguration Configuration { get; set; } public static NLog.ILogger AppLogger { get; } = LogManager.GetLogger("Logger"); private static readonly ILogger Log = LogManager.GetCurrentClassLogger(); public static void BuildContainer(ContainerBuilder builder) { #region Container configuration try { #region Library Configuration builder.RegisterInstance(AppLogger) .As<NLog.ILogger>() .SingleInstance(); #endregion #region Application specific types builder.RegisterOptions<ServiceEntryPointOptions>(Configuration, ServiceEntryPointOptions.ConfigSectionName); // Used by DeadletterErrorHandler builder.RegisterType<ServiceStoppingService>() .As<IServiceStoppingService>() .SingleInstance(); builder.RegisterType<LifecycleService>().As<ILifecycleAsync>(); builder.RegisterRmqConnectionManager(Configuration, RmqConnectionManagerOptions.ConfigSectionName); builder.RegisterRmqMessageHandler(Configuration, RmqMessageHandlerOptions.ConfigSectionName); builder.RegisterEmailNotificationServiceOptions(Configuration, EmailNotificationServiceOptions.ConfigSectionName); #endregion } catch (Exception e) { Log.Error(e); throw; } #endregion } }
AutofacConfigurationExtension.cs
public static class AutofacConfigurationExtensions { public static ContainerBuilder RegisterOptions<TOptions>( this ContainerBuilder builder, IConfiguration config, string sectionName, string name = null ) where TOptions : class, new() { var section = config.GetRequiredSection(sectionName); var options = section.Get<TOptions>(); var accessor = new OptionsWrapper<TOptions>(options); var item = builder.RegisterInstance(options); item = name switch { null => item.As<TOptions>(), _ => item.Named<TOptions>(name) }; item.SingleInstance(); var item2 = builder.RegisterInstance(accessor); item2 = name switch { null => item2.As<IOptions<TOptions>>(), _ => item2.Named<IOptions<TOptions>>(name) }; item2.SingleInstance(); return builder; } public static ContainerBuilder RegisterRmqConnectionManager(this ContainerBuilder builder, IConfiguration config, string sectionName, string name = null) { builder.RegisterOptions<RmqConnectionManagerOptions>(config, sectionName, name); builder.RegisterType<RmqConnectionManager>() .As<IRmqConnectionManager>() .PropertiesAutowired(); return builder; } public static ContainerBuilder RegisterRmqMessageHandler(this ContainerBuilder builder, IConfiguration config, string sectionName, string name = null) { builder.RegisterType<DbRepository>() .As<IDomainRepository>(); builder.RegisterType<FileParser>() .As<IFileParser>(); builder.RegisterType<FileReader>() .As<IFileReader>(); builder.RegisterOptions<RmqMessageHandlerOptions>(config, sectionName, name); builder.RegisterType<RmqMessageHandler>() .As<IRmqMessageHandler>() .SingleInstance() .PropertiesAutowired(); return builder; } public static ContainerBuilder RegisterEmailNotificationServiceOptions(this ContainerBuilder builder, IConfiguration config, string sectionName, string name = null) { builder.RegisterOptions<EmailNotificationServiceOptions>(config, sectionName, name); builder.RegisterType<EmailNotificationService>() .As<IEmailNotificationService>() .SingleInstance() .PropertiesAutowired(); return builder; } }
可能的原因
- 异常被静默吞噬:Main方法的catch块为空,容器构建、服务启动过程中若发生异常,会被完全隐藏,导致RunAsync无法正常启动但无报错信息。建议在catch块添加日志输出(如
log.Fatal(e, "应用启动失败");),并重新抛出异常便于排查。 - 静态字段线程安全问题:
WindowsBackgroundService.WindowsServiceKey是静态字段,在配置读取后赋值,若存在多线程竞争(虽Main是单线程启动,但容器构建可能涉及异步操作),可能导致服务名称设置异常,引发Windows服务启动阻塞。可尝试硬编码服务名称测试,排除配置读取影响。 - Autofac静态配置线程风险:
AutofacConfiguration.Configuration为静态字段,在UseServiceProviderFactory中赋值,若容器构建过程中存在并发访问,可能导致配置值异常,进而影响服务注册。建议将配置作为参数传入BuildContainer,而非依赖静态字段。 - 基础服务初始化阻塞:即使仅保留日志服务,NLog加载
NLog.config时若遇到IO问题(如文件被占用、权限不足),可能导致初始化卡住。可临时移除NLog初始化,改用控制台日志测试是否能正常启动。 - HostedService启动死锁:
WindowsBackgroundService的StartAsync方法若存在同步阻塞操作(如无限循环等待资源、未正确使用异步API),会导致RunAsync挂起。需检查该服务的实现逻辑,确保异步方法无阻塞。
内容的提问来源于stack exchange,提问作者ravikiran
相关产品推荐
相关产品推荐

