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

使用Task.WaitAll获取并发异步API调用结果的问题排查

健康检查API异步调用问题分析与修复

核心问题梳理

  • 同步等待引发阻塞/死锁风险:ExecuteApiCallsAsync中使用Task.WaitAll属于同步等待逻辑,在异步方法里应替换为await Task.WhenAll,否则极易在ASP.NET、UI线程等环境中引发线程阻塞甚至死锁。
  • 返回值类型错误:直接返回tasks.ToList()得到的是List<Task<HealthStatus>>,并非业务期望的List<HealthStatus>,需要显式获取任务的执行结果。
  • 空catch块吞异常:ExecuteApiCallsAsync的catch块未做任何处理,既不记录异常也不返回有效结果,会直接导致编译错误(方法必须返回指定类型的值)。
  • HttpClient滥用:GetApiHealthStatusAsync每次调用都创建新的HttpClient实例,频繁创建销毁会耗尽系统套接字资源,必须复用客户端实例。
  • 非成功响应未处理:当API返回非成功状态码时,未设置对应的健康状态,导致状态数据不完整。
  • 反序列化异常未针对性处理:JSON反序列化失败时,虽有外层catch兜底,但未明确标记状态,不利于问题排查。

修复后的代码示例

修正ExecuteApiCallsAsync方法

public async Task<List<HealthStatus>> ExecuteApiCallsAsync()
{
    var apis = new[] {
         new { Name = "API 1", Url = "https://localhost:7157/api/healthstatus" },
         new { Name = "API 2", Url = "https://localhost:7150/api/healthstatus" }
    };

    var tasks = new List<Task<HealthStatus>>();

    try
    {
        foreach (var api in apis)
        {
            tasks.Add(GetApiHealthStatusAsync(api.Name, api.Url));
        }

        // 异步等待所有任务完成,直接获取结果数组
        var results = await Task.WhenAll(tasks);
        return results.ToList();
    }
    catch (Exception ex)
    {
        // 记录全局异常,返回空列表保证方法完整性
        Console.WriteLine($"全局执行异常:{ex.Message}");
        return new List<HealthStatus>();
    }
}

优化GetApiHealthStatusAsync方法(复用HttpClient)

// 复用HttpClient实例,生产环境建议通过依赖注入IHttpClientFactory获取
private static readonly HttpClient _httpClient = new HttpClient();

public async Task<HealthStatus> GetApiHealthStatusAsync(string apiName, string apiUrl)
{
    var healthStatus = new HealthStatus { Name = apiName };

    try
    {
        var result = await _httpClient.GetAsync(apiUrl);

        if (result.IsSuccessStatusCode)
        {
            var content = await result.Content.ReadAsStringAsync();
            // 单独捕获反序列化异常,避免影响整体逻辑
            try
            {
                var deserializedStatus = JsonConvert.DeserializeObject<HealthStatus>(content);
                healthStatus = deserializedStatus ?? healthStatus;
            }
            catch (JsonException ex)
            {
                Console.WriteLine($"API {apiName} 响应反序列化失败:{ex.Message}");
                healthStatus.Condition = "Degraded";
            }
        }
        else
        {
            // 处理非成功状态码,明确标记状态
            healthStatus.Condition = $"Unhealthy (状态码:{(int)result.StatusCode})";
        }
    }
    catch (HttpRequestException ex)
    {
        Console.WriteLine($"API {apiName} 请求失败:{ex.Message}");
        healthStatus.Condition = "Unhealthy";
    }
    catch (Exception ex)
    {
        Console.WriteLine($"API {apiName} 未知错误:{ex.Message}");
        healthStatus.Condition = "Unhealthy";
    }
    return healthStatus;
}

关键优化说明

  1. 异步等待替代同步等待:await Task.WhenAll遵循异步编程最佳实践,不会阻塞线程,彻底规避死锁风险。
  2. 正确获取任务结果:Task.WhenAll直接返回所有任务的结果数组,转换为List即可得到业务需要的健康状态集合。
  3. 复用HttpClient:静态HttpClient实例(或通过IHttpClientFactory注入)避免频繁创建销毁带来的资源浪费,提升性能。
  4. 完善状态与异常处理:分别处理HTTP请求异常、反序列化异常、非成功状态码,确保每个API的健康状态都能准确返回。
  5. 避免异常吞灭:catch块添加日志记录,便于排查问题,同时返回有效结果保证方法的完整性。

内容的提问来源于stack exchange,提问作者N D

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.27 07:25:31