Async Task.WhenAll with timeout

后端 未结 11 1968
梦谈多话
梦谈多话 2020-11-29 04:33

Is there a way in the new async dotnet 4.5 library to set a timeout on the Task.WhenAll method. I want to fetch several sources and stop after say 5 seconds and

相关标签:
11条回答
  • 2020-11-29 05:02

    In addition to timeout, I also check the cancellation which is useful if you are building a web app.

    public static async Task WhenAll(
        IEnumerable<Task> tasks, 
        int millisecondsTimeOut,
        CancellationToken cancellationToken)
    {
        using(Task timeoutTask = Task.Delay(millisecondsTimeOut))
        using(Task cancellationMonitorTask = Task.Delay(-1, cancellationToken))
        {
            Task completedTask = await Task.WhenAny(
                Task.WhenAll(tasks), 
                timeoutTask, 
                cancellationMonitorTask
            );
    
            if (completedTask == timeoutTask)
            {
                throw new TimeoutException();
            }
            if (completedTask == cancellationMonitorTask)
            {
                throw new OperationCanceledException();
            }
            await completedTask;
        }
    }
    
    0 讨论(0)
  • 2020-11-29 05:07

    You could combine the resulting Task with a Task.Delay() using Task.WhenAny():

    await Task.WhenAny(Task.WhenAll(tasks), Task.Delay(timeout));
    

    If you want to harvest completed tasks in case of a timeout:

    var completedResults =
      tasks
      .Where(t => t.Status == TaskStatus.RanToCompletion)
      .Select(t => t.Result)
      .ToList();
    
    0 讨论(0)
  • 2020-11-29 05:09

    I think a clearer, more robust option that also does exception handling right would be to use Task.WhenAny on each task together with a timeout task, go through all the completed tasks and filter out the timeout ones, and use await Task.WhenAll() instead of Task.Result to gather all the results.

    Here's a complete working solution:

    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);
    }
    
    0 讨论(0)
  • 2020-11-29 05:10

    What you describe seems like a very common demand however I could not find anywhere an example of this. And I searched a lot... I finally created the following:

    TimeSpan timeout = TimeSpan.FromSeconds(5.0);
    
    Task<Task>[] tasksOfTasks =
    {
        Task.WhenAny(SomeTaskAsync("a"), Task.Delay(timeout)),
        Task.WhenAny(SomeTaskAsync("b"), Task.Delay(timeout)),
        Task.WhenAny(SomeTaskAsync("c"), Task.Delay(timeout))
    };
    
    Task[] completedTasks = await Task.WhenAll(tasksOfTasks);
    
    List<MyResult> = completedTasks.OfType<Task<MyResult>>().Select(task => task.Result).ToList();
    

    I assume here a method SomeTaskAsync that returns Task<MyResult>.

    From the members of completedTasks, only tasks of type MyResult are our own tasks that managed to beat the clock. Task.Delay returns a different type. This requires some compromise on typing, but still works beautifully and quite simple.

    (The array can of course be built dynamically using a query + ToArray).

    • Note that this implementation does not require SomeTaskAsync to receive a cancellation token.
    0 讨论(0)
  • 2020-11-29 05:10

    I came to the following piece of code that does what I needed:

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Threading.Tasks;
    using System.Net.Http;
    using System.Json;
    using System.Threading;
    
    namespace MyAsync
    {
        class Program
        {
            static void Main(string[] args)
            {
                var cts = new CancellationTokenSource();
                Console.WriteLine("Start Main");
                List<Task<List<MyObject>>> listoftasks = new List<Task<List<MyObject>>>();
                listoftasks.Add(GetGoogle(cts));
                listoftasks.Add(GetTwitter(cts));
                listoftasks.Add(GetSleep(cts));
                listoftasks.Add(GetxSleep(cts));
    
                List<MyObject>[] arrayofanswers = Task.WhenAll(listoftasks).Result;
                List<MyObject> answer = new List<MyObject>();
                foreach (List<MyObject> answers in arrayofanswers)
                {
                    answer.AddRange(answers);
                }
                foreach (MyObject o in answer)
                {
                    Console.WriteLine("{0} - {1}", o.name, o.origin);
                }
                Console.WriteLine("Press <Enter>");
                Console.ReadLine();
            } 
    
            static async Task<List<MyObject>> GetGoogle(CancellationTokenSource cts) 
            {
                try
                {
                    Console.WriteLine("Start GetGoogle");
                    List<MyObject> l = new List<MyObject>();
                    var client = new HttpClient();
                    Task<HttpResponseMessage> awaitable = client.GetAsync("http://ajax.googleapis.com/ajax/services/search/web?v=1.0&q=broersa", cts.Token);
                    HttpResponseMessage res = await awaitable;
                    Console.WriteLine("After GetGoogle GetAsync");
                    dynamic data = JsonValue.Parse(res.Content.ReadAsStringAsync().Result);
                    Console.WriteLine("After GetGoogle ReadAsStringAsync");
                    foreach (var r in data.responseData.results)
                    {
                        l.Add(new MyObject() { name = r.titleNoFormatting, origin = "google" });
                    }
                    return l;
                }
                catch (TaskCanceledException)
                {
                    return new List<MyObject>();
                }
            }
    
            static async Task<List<MyObject>> GetTwitter(CancellationTokenSource cts)
            {
                try
                {
                    Console.WriteLine("Start GetTwitter");
                    List<MyObject> l = new List<MyObject>();
                    var client = new HttpClient();
                    Task<HttpResponseMessage> awaitable = client.GetAsync("http://search.twitter.com/search.json?q=broersa&rpp=5&include_entities=true&result_type=mixed",cts.Token);
                    HttpResponseMessage res = await awaitable;
                    Console.WriteLine("After GetTwitter GetAsync");
                    dynamic data = JsonValue.Parse(res.Content.ReadAsStringAsync().Result);
                    Console.WriteLine("After GetTwitter ReadAsStringAsync");
                    foreach (var r in data.results)
                    {
                        l.Add(new MyObject() { name = r.text, origin = "twitter" });
                    }
                    return l;
                }
                catch (TaskCanceledException)
                {
                    return new List<MyObject>();
                }
            }
    
            static async Task<List<MyObject>> GetSleep(CancellationTokenSource cts)
            {
                try
                {
                    Console.WriteLine("Start GetSleep");
                    List<MyObject> l = new List<MyObject>();
                    await Task.Delay(5000,cts.Token);
                    l.Add(new MyObject() { name = "Slept well", origin = "sleep" });
                    return l;
                }
                catch (TaskCanceledException)
                {
                    return new List<MyObject>();
                }
    
            } 
    
            static async Task<List<MyObject>> GetxSleep(CancellationTokenSource cts)
            {
                Console.WriteLine("Start GetxSleep");
                List<MyObject> l = new List<MyObject>();
                await Task.Delay(2000);
                cts.Cancel();
                l.Add(new MyObject() { name = "Slept short", origin = "xsleep" });
                return l;
            } 
    
        }
    }
    

    My explanation is in my blogpost: http://blog.bekijkhet.com/2012/03/c-async-examples-whenall-whenany.html

    0 讨论(0)
提交回复
热议问题