Async Task.WhenAll с таймаутом - PullRequest
       58

Async Task.WhenAll с таймаутом

52 голосов
/ 24 марта 2012

Есть ли способ в новой библиотеке async dotnet 4.5 установить время ожидания для метода Task.WhenAll. Я хочу получить несколько источников и через 5 секунд остановиться и пропустить источники, которые не были закончены.

Ответы [ 11 ]

73 голосов
/ 24 марта 2012

Вы можете объединить получившиеся Task с Task.Delay(), используя Task.WhenAny():

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

Если вы хотите собрать выполненные задания в случае тайм-аута:

var completedResults =
  tasks
  .Where(t => t.Status == TaskStatus.RanToCompletion)
  .Select(t => t.Result)
  .ToList();
18 голосов
/ 09 сентября 2014

Я думаю, что более ясный, более надежный вариант, который также делает обработку исключений правильным , будет использовать Task.WhenAny для каждой задачи вместе с задачей тайм-аута , пройдите все выполненные задачи и отфильтруйте тайм-ауты, и используйте await Task.WhenAll() вместо Task.Result, чтобы собрать все результаты.

Вот полное рабочее решение:

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);
}
9 голосов
/ 24 марта 2012

Ознакомьтесь с разделами «Ранний выход из кризиса» и «Task.Delay» из обзора асинхронных шаблонов Microsoft на основе задач .

Ранняя помощь. Операция, представленная как t1, может быть сгруппирована в WhenAny с другой задачей t2, и мы можем ждать задачи WhenAny. t2 может представлять собой тайм-аут, или отмену, или какой-либо другой сигнал, который приведет к завершению задачи WhenAny до завершения t1.

2 голосов
/ 07 августа 2015

В дополнение к таймауту я также проверяю отмену, которая полезна, если вы создаете веб-приложение.

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;
    }
}
2 голосов
/ 01 сентября 2014

То, что вы описываете, кажется очень распространенным требованием, однако я нигде не мог найти пример этого. И я много искал ... Я наконец-то создал следующее:

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();

Я предполагаю, что здесь метод SomeTaskAsync, который возвращает задачу .

Из числа выполненных задач, только задачи типа MyResult являются нашими собственными задачами, которым удалось побить время. Task.Delay возвращает другой тип. Это требует некоторого компромисса при наборе текста, но все равно работает красиво и довольно просто.

(Конечно, массив можно построить динамически, используя запрос + ToArray).

  • Обратите внимание, что для этой реализации не требуется SomeTaskAsync для получения токена отмены.
1 голос
/ 18 октября 2018

недействительная версия результата ответа @ i3arnon, а также комментарии и изменение первого аргумента для использования расширения this.

У меня также есть метод пересылки, задающий время ожидания в виде целого числа с использованием TimeSpan.FromMilliseconds(millisecondsTimeout) для сопоставлениядругие методы задачи.

public static async Task WhenAll(this IEnumerable<Task> tasks, TimeSpan timeout)
{
  // Create a timeout task.
  var timeoutTask = Task.Delay(timeout);

  // Get the completed tasks made up of...
  var completedTasks =
  (
    // ...all tasks specified
    await Task.WhenAll(tasks

    // Now finish when its task has finished or the timeout task finishes
    .Select(task => Task.WhenAny(task, timeoutTask)))
  )
  // ...but not the timeout task
  .Where(task => task != timeoutTask);

  // And wait for the internal WhenAll to complete.
  await Task.WhenAll(completedTasks);
}
0 голосов
/ 12 июля 2019

Вы можете использовать следующий код:

        var timeoutTime = 10;

        var tasksResult = await Task.WhenAll(
                                listOfTasks.Select(x => Task.WhenAny(
                                    x, Task.Delay(TimeSpan.FromMinutes(timeoutTime)))
                                )
                            );


        var succeededtasksResponses = tasksResult
                                               .OfType<Task<MyResult>>()
                                               .Select(task => task.Result);

        if (succeededtasksResponses.Count() != listOfTasks.Count())
        {
            // Not all tasks were completed
            // Throw error or do whatever you want
        }

        //You can use the succeededtasksResponses that contains the list of successful responses

Как это работает:

Вам необходимо указать в переменной timeoutTime ограничение времени для выполнения всех задач.Таким образом, в основном все задачи будут ожидать максимум времени, которое вы установили в timeoutTime.Когда все задачи возвращают результат, тайм-аут не наступит, и будет установлен taskResult.

После этого мы получаем только выполненные задачи.Задачи, которые не были выполнены, не будут иметь результатов.

0 голосов
/ 29 мая 2014

В дополнение к ответу svick у меня работает следующее, когда мне нужно подождать пару задач для завершения, но мне придется обрабатывать что-то еще, пока я жду:

Task[] TasksToWaitFor = //Your tasks
TimeSpan Timeout = TimeSpan.FromSeconds( 30 );

while( true )
{
    await Task.WhenAny( Task.WhenAll( TasksToWaitFor ), Task.Delay( Timeout ) );
    if( TasksToWaitFor.All( a => a.IsCompleted ) )
        break;

    //Do something else here
}
0 голосов
/ 04 апреля 2014

Ознакомьтесь с пользовательским комбинатором задач, предложенным в http://tutorials.csharp -online.net / Task_Combinators

async static Task<TResult> WithTimeout<TResult> 
   (this Task<TResult> task, TimeSpan timeout)
 {
   Task winner = await (Task.WhenAny 
      (task, Task.Delay (timeout)));
   if (winner != task) throw new TimeoutException();
   return await task; // Unwrap result/re-throw
}

Я еще не пробовал.

0 голосов
/ 26 марта 2012

Я пришел к следующему коду, который делает то, что мне нужно:

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;
        } 

    }
}

Мое объяснение в моем блоге: http://blog.bekijkhet.com/2012/03/c-async-examples-whenall-whenany.html

...