使用 Task.WaitAll 检索并发异步 API 调用的结果时出现问题

Problem with retrieving the results of concurrent asynchronous API calls using Task.WaitAll

提问人:N D 提问时间:3/22/2023 最后编辑:Theodor ZouliasN D 更新时间:3/23/2023 访问量:44

问:

我正在开发一个检查外部服务(API等)健康状况的应用程序。 为了显示我需要的运行状况,我需要对每个服务进行 API 调用。

我在使用异步函数检索 API 调用的结果时遇到问题。

我有两种方法可以检索 API 端点的信息。但我不明白为什么它不起作用。

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
    {
        //    for(int i = 0; i < apis.Length; i++)
        //    {
        //        //tasks[i] = GetApiHealthStatusAsync(apis[i].Name, apis[i].Url);
        //        //tasks.Add(GetApiHealthStatusAsync(api.Name, api.Url));
        //    }
        foreach (var api in apis)
        {
            tasks.Add(GetApiHealthStatusAsync(api.Name, api.Url));
        }

        Task.WaitAll(tasks.ToArray());
        
        return tasks.ToList();
    }
    catch (Exception ex)
    {
    }
}

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

    try
    {
        using var client = new HttpClient();
        var result = await client.GetAsync(apiUrl);

        if (result != null && result.IsSuccessStatusCode)
        {
            var content = await result.Content.ReadAsStringAsync();
            healthStatus = JsonConvert.DeserializeObject<HealthStatus>(content);
        }
    }
    catch (Exception ex)
    {
        Console.WriteLine(ex.Message);
        healthStatus.Condition = "Unhealthy";
    }
    return healthStatus;
}
C# asp.net .NET 异步 并发

评论

5赞 Damien_The_Unbeliever 3/22/2023
空块是一个不健康的迹象。它们是你告诉系统“我不在乎这里的任何错误”的方式,并且由于显然你遇到了一些(尚未提及的)错误的问题,它们可能不属于你的代码。catch
1赞 Theodor Zoulias 3/23/2023
你有什么问题?中方能否介绍有关情况?另外,请提及你所面向的 .NET 平台和版本 (.NET Framework?, .NET 7?)。
0赞 Aluan Haddad 3/23/2023
这应该会产生警告。阅读它们。

答:

0赞 Md. Rayhan Khan 3/23/2023 #1

问题似乎出在以下代码行上:

返回任务。ToList(); tasks 是一个 Task 对象列表,对它调用 ToList() 将简单地创建一个具有相同 Task 对象的新列表。若要实际检索任务的结果,需要对每个 Task 对象调用 Result 属性。

一种方法是使用 await Task.WhenAll(tasks) 方法而不是 Task.WaitAll(tasks.ToArray())。这将等待所有任务完成,并将其结果作为 HealthStatus 对象数组返回。

下面是修改后的 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)
    {
        // Handle exception
        return null;
    }
}

请注意,您还应该正确处理异常,方法是重新引发异常或返回 null。