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

WPF集成SimpleInjector时HttpClient.GetAsync调用挂起问题求助

问题根源:异步操作阻塞导致的死锁

你的问题是典型的WPF UI线程死锁,原因在于:

  • WPF的UI线程带有SynchronizationContext,异步方法中await会捕获这个上下文,任务完成后试图回到UI线程继续执行。
  • 你在SetupService的构造函数中调用RefreshSetup,里面用.Result阻塞了UI线程等待异步任务;但await client.GetAsync(endpoint)需要回到被阻塞的UI线程,导致双方互相等待,形成死锁。
解决办法(保留SimpleInjector机制)

核心思路:把异步初始化逻辑从构造函数移到独立的异步方法中,避免在UI线程中阻塞等待异步任务。

1. 修改SetupService,拆分异步初始化逻辑

移除构造函数中的RefreshSetup调用,新增异步初始化方法,用await替代.Result:

public class SetupService: ISetupService
{
    private static readonly Dictionary<string, string> AcceptType = new Dictionary<string, string>
    {
        {"Accept", "application/xml"}
    };
    private const string AuthenticationType = "Basic";

    private readonly IServiceClient _serviceClient;
    private readonly ILoggingProvider _logger;
    private readonly IConfigurationSection _configuration;
    private SetupResponse _response;

    // 构造函数仅做依赖注入,不执行异步逻辑
    public SetupService(IConfigurationSection configuration, IServiceClient serviceClient, ILoggingProvider logger)
    {
        _serviceClient = serviceClient;
        _logger = logger;
        _configuration = configuration;
    }

    // 异步初始化方法
    public async Task InitAsync()
    {
        try
        {
            var token = BuildIdentityToken();

            var authHeaderClear = string.Format("IDENTITY_TOKEN:{0}", token);

            var authenticationHeaderValue =
                new AuthenticationHeaderValue(AuthenticationType, Convert.ToBase64String(Encoding.ASCII.GetBytes(authHeaderClear)));

            _serviceClient.Url = _configuration.Url;
            // 用await替代.Result,避免阻塞
            var httpResponse = await _serviceClient.GetAsync(string.Empty, authenticationHeaderValue, AcceptType);

            var responseString = await httpResponse.Content.ReadAsStringAsync();

            _response = responseString.FromXML<SetupResponse>();
        }
        catch (Exception e)
        {
            _logger?.LogError(e, "初始化Setup失败");
            throw;
        }
    }

    // 保留手动刷新的异步方法(如果需要)
    public async Task RefreshSetupAsync()
    {
        await InitAsync();
    }
}

2. 修改App.xaml.cs,异步执行初始化

将OnStartup改为异步方法,注册依赖后手动调用SetupService的异步初始化:

public partial class App : Application
{
    private static readonly Container _container = new Container();

    // 事件处理方法允许使用async void
    protected override async void OnStartup(StartupEventArgs e)
    {
        base.OnStartup(e);
        RegisterDependencies();
        _container.Verify();

        // 获取SetupService实例并执行异步初始化
        var setupService = _container.GetInstance<ISetupService>();
        await setupService.InitAsync();
    }

    private void RegisterDependencies()
    {
        var serviceConfigSection = ServiceConfigurationSection.Get();
        
        _container.RegisterSingle<ILoggingProvider, LoggingProvider>();
        _container.RegisterSingle<IServiceClient>(() => new ServiceClient(_container.GetInstance<ILoggingProvider>()));
        _container.RegisterSingle<IConfigurationSection>(() => SetupConfigurationSection.Get());
        _container.RegisterSingle<ISetupService, SetupService>();
    }
}

3. 额外优化建议

  • 复用HttpClient实例:当前ServiceClient每次创建新的HttpClient,建议将HttpClient注册为Singleton注入到ServiceClient中,避免Socket连接耗尽问题。
  • 禁止构造函数执行异步/阻塞操作:构造函数仅负责依赖赋值,复杂初始化(尤其是IO、异步操作)必须放到专门的方法中,这是依赖注入的通用最佳实践。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.11 15:41:15