【问题标题】:Multiple Async tasks with timeout and results具有超时和结果的多个异步任务
【发布时间】:2017-08-29 03:35:35
【问题描述】:

我是异步世界的新手。关于你的静态方法(来自https://stackoverflow.com/a/25733275/1596974):

static async Task<TResult[]> WhenAll<TResult>(IEnumerable<Task<TResult>> tasks, TimeSpan timeout)
{
    var timeoutTask = Task.Delay(timeout).ContinueWith(_ => default(TResult));
    var completedTasks = 
        (await Task.WhenAll(tasks.Select(task => Task.WhenAny(task, timeoutTask)))).
        Where(task => task != timeoutTask);
    return await Task.WhenAll(completedTasks);
}

我应该如何使用它来检索这些任务的结果? 为了清楚起见,我需要在这里实现的基本上是这样的:

  • 对于每项任务,我都会致电多个运输提供商,以便从他们那里获得不同的运费。
  • 将所有运输提供商的响应汇总到一个大的运费列表中。 有时,一个(或多个)运输提供商可能会倒闭。因此,我需要从成功完成的任务中检索运费并跳过失败的任务。 我希望我已经足够清楚了。

【问题讨论】:

    标签: async-await


    【解决方案1】:

    好的,我最终得到了这个运行良好的代码:

    var providers = GetShippingProviders().ToList();
    var tasks = new Task<Task>[providers.Count];
    var timeout = TimeSpan.FromMilliseconds(10000);    
    
    try
    {
        var shippingRates = new List<IShippingRate>();
    
        for (var i = 0; i < tasks.Length; i++)
        {
            var provider = providers[i];
            tasks[i] = Task.WhenAny(Task.Run(() => provider.GetShippingRates(origin, destination, weight)), Task.Delay(timeout));
        }
    
        Task.WaitAll(tasks);
    
        foreach (var tasksResult in tasks.Select(x => x.Result).Where(x => x.Status == TaskStatus.RanToCompletion))
        {
            var shippingRatesResult = tasksResult as Task<List<IShippingRate>>;
            if (shippingRatesResult != null)
                shippingRates.AddRange(shippingRatesResult.Result.ToList());
        }
    }
    catch (AggregateException ae)
    {
        Log.Error("An exception occurred when retrieving the shipping Rates.", ae.Flatten());        
    }
    

    所有成功完成的任务都会被处理。那些失败的可以跳过。有一种方法可以使用“Task.ContinueWith(...)”添加一些代码,这样出错的任务也可以被捕获以记录异常。

    【讨论】:

      【解决方案2】:

      对于那些失败的,他们会抛出异常吗?还是他们只是挂了很长时间?

      我可能会这样做:

      var shippingProviderRateTasks = ...;
      var results = ...;
      
      foreach (var task in shippingProviderRateTasks) {
          try {
              results.Add(await task);
          } catch (Exception e) {
              // log the error here, if you want, and skip this provider
          }
      }
      

      使用 Task.WhenAll 的主要原因之一是在异常发生时捕获它,而不是稍后捕获。如果您想吞下所有异常并从根本上忽略这些错误,您不妨一次一个地等待它们——它不应该更慢。

      【讨论】:

        猜你喜欢
        • 2014-11-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2018-07-23
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多