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

前端 未结 2 559
渐次进展
渐次进展 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:52

    Simply pass to all of your tasks the same cancellation token, something like this:

    CancellationTokenSource cts = new CancellationTokenSource();
    CancellationToken ct = cts.Token;
    // here you specify how long you want to wait for task to finish before cancelling
    int timeout = 5000;
    cts.CancelAfter(timeout);
    // pass ct to all your tasks and start them
    await Task.WhenAny(/* your tasks here */);
    // cancel all tasks
    cts.Cancel();
    

    Also, you need to read this thread to be aware of how to use CancellationToken correctly: When I use CancelAfter(), the Task is still running

    0 讨论(0)
  • 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();
    
    0 讨论(0)
提交回复
热议问题