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

为何使用Task.WhenAll并行调用后返回值为null?

并行调用Task.WhenAll后返回null的排查与解决

原本串行调用两个异步方法可正常获取预期数据,改为Task.WhenAll并行调用后,所有方法均返回null。在Visual Studio中查看Task对象,属性显示Id=2356、Status=RanToCompletion、Method={null}、Result=""。

原串行代码

private async Task<Entity> MethodAsync()
{
    ...
    var model1 = await client.Method1(Id1, cancellationToken);            
    var model2 = await client.Method2(Id2, cancellationToken);
    ...
}

并行调用代码(多种写法均无效)

private async Task<Entity> MethodAsync()
{
     var model1Task = client.Method1(Id1, cancellationToken);
     var model2Task = client.Method2(Id2, cancellationToken);
     var task = Task.WhenAll(new Task[] { model1Task ,model2Task });         
     await task;     //doesn't work
     //task.Wait();  //doesn't work
     //await Task.WhenAll(new Task[] { model1Task , model2Task }); //doesn't work
     //Task.WhenAll(new Task[] { model1Task, model2Task}); //doesn't work
}

客户端方法示例(Method2逻辑类似)

public async Task<Model> Method1(Guid Id, CancellationToken cancellationToken)
{
    HttpResponseMessage responseMessage = await client.GetAsync($"customEndPoint");
    ResponseMessageSingle<Model> response = JsonSerializer.Deserialize<ResponseMessageSingle<Model>>(
        await responseMessage.Content.ReadAsStringAsync(cancellationToken),
        new JsonSerializerOptions(JsonSerializerDefaults.Web));

    return response.result;
}       

private class ResponseMessageSingle<T>
{
    public bool success { get; set; }
    public string message { get; set; }
    public T result { get; set; }
}

自定义授权拦截器(DelegatingHandler)

protected override async Task<HttpResponseMessage> SendAsync(
          HttpRequestMessage request, CancellationToken cancellationToken)
 {
      some logic...
      request.SetBearerToken(TokenResponse.AccessToken);
      HttpResponseMessage response = await base.SendAsync(request, cancellationToken);    
      return await Task.FromResult(response);
 }

问题排查与解决方案

1. 修复拦截器的线程安全与冗余代码

拦截器中return await Task.FromResult(response);属于冗余代码,base.SendAsync已返回Task<HttpResponseMessage>,直接返回response即可,多余的包装不会引发错误,但会增加不必要的开销。

更关键的是:如果TokenResponse是多个请求共享的状态对象,并行调用时可能存在线程安全问题——并发请求可能同时修改或读取该对象,导致部分请求的Bearer Token无效,最终返回的响应中result为null。

修复后的拦截器代码:

protected override async Task<HttpResponseMessage> SendAsync(
      HttpRequestMessage request, CancellationToken cancellationToken)
{
    // 确保TokenResponse的访问是线程安全的,比如使用lock或线程安全容器
    lock (TokenResponse)
    {
        request.SetBearerToken(TokenResponse.AccessToken);
    }
    HttpResponseMessage response = await base.SendAsync(request, cancellationToken);    
    return response; // 移除冗余的Task.FromResult包装
}

2. 完善客户端方法的请求与响应处理

原客户端方法存在两个问题:

  • GetAsync未传入cancellationToken,无法在并行场景下正确响应取消操作;
  • 未检查HTTP响应状态,若请求返回错误码(如401、500),反序列化后response.result会直接为null,无任何错误提示。

修复后的客户端方法:

public async Task<Model> Method1(Guid Id, CancellationToken cancellationToken)
{
    // 传入cancellationToken到GetAsync
    HttpResponseMessage responseMessage = await client.GetAsync($"customEndPoint", cancellationToken);
    // 检查响应是否成功,失败则抛出异常
    responseMessage.EnsureSuccessStatusCode();
    
    var responseContent = await responseMessage.Content.ReadAsStringAsync(cancellationToken);
    ResponseMessageSingle<Model> response = JsonSerializer.Deserialize<ResponseMessageSingle<Model>>(
        responseContent,
        new JsonSerializerOptions(JsonSerializerDefaults.Web));

    // 检查业务响应是否成功
    if (!response.success)
    {
        throw new InvalidOperationException($"请求失败:{response.message}");
    }

    return response.result;
}

3. 修正并行代码的写法

并行代码无需将任务包装为Task[],直接传入泛型Task即可,且需明确获取每个任务的结果:

private async Task<Entity> MethodAsync()
{
    var model1Task = client.Method1(Id1, cancellationToken);
    var model2Task = client.Method2(Id2, cancellationToken);
    
    // 等待所有任务完成
    await Task.WhenAll(model1Task, model2Task);
    
    // 获取任务结果(或直接await每个任务,效果一致)
    var model1 = model1Task.Result;
    var model2 = model2Task.Result;
    
    // 组装Entity并返回
    ...
}

4. 验证响应内容

若以上修复无效,可在客户端方法中输出responseContent的内容,确认返回的JSON中result字段是否确实为null——这能帮你快速定位是请求层面的问题(如Token无效、接口返回错误),还是反序列化层面的问题。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.16 13:40:40