Task.WhenAny with cancellation of the non completed tasks and timeout

前端 未结 2 558
渐次进展
渐次进展 2020-12-20 18:03

In my app I am creating some concurrent web requests and I am satisfied when any one of them completes, so I am using the method Task.WhenAny:

var urls = new         


        
2条回答
  •  醉梦人生
    2020-12-20 18:53

    Update: better solution based on Stephen Cleary's answer and MSDN and svick's answer:

    CancellationTokenSource source = new CancellationTokenSource();
    source.CancelAfter(TimeSpan.FromSeconds(1));
    
    var tasks = urls.Select(url => Task.Run( async () => 
    {
        using (var webClient = new WebClient())
        {
            token.Register(webClient.CancelAsync);
            var result = (Url: url, Data: await webClient.DownloadStringTaskAsync(url));
            token.ThrowIfCancellationRequested();
            return result.Url;
        }
    }, token)).ToArray();
    
    string url;
    try
    {
        // (A canceled task will raise an exception when awaited).
        var firstTask = await Task.WhenAny(tasks);
        url = (await firstTask).Url;
    }   
    catch (AggregateException ae) {
       foreach (Exception e in ae.InnerExceptions) {
          if (e is TaskCanceledException)
             Console.WriteLine("Timeout: {0}", 
                               ((TaskCanceledException) e).Message);
          else
             Console.WriteLine("Exception: " + e.GetType().Name);
       }
    }
    

    non-optimal solution

    The timeout can be solved by adding a task that just waits and completes after given time. Then you check which task completed first, if it is the waiting one, then timeout effectively occurred.

    Task timeout = Task.Delay(10000);
    var firstTask = await Task.WhenAny(tasks.Concat(new Task[] {timeout}));
    if(firstTask == timeout) { ... } //timed out
    source.Cancel();
    

提交回复
热议问题