Параллельный запуск нескольких задач, каждая из которых имеет свой тайм-аут - PullRequest
1 голос
/ 24 апреля 2019

У меня есть несколько похожих задач, каждая из которых ограничена тайм-аутом, то есть должна быть выполнена быстрее указанного времени или вернуть пустой результат.Основная цель этих задач - получение ответа от сервера с ограничением времени ожидания.Ниже приведен пример такой задачи:

       public async Task<List<Data>> GetDataWithTimeoutAsync(InputData data, int timeout)
        {   
            List<Data> resultData = new List<Data>;
            await Task.WhenAny(Task.Run(async () =>
                {
                    resultData.Add(SomeWork(data));
                }),
                Task.Delay(timeout));

            return resultData;
        }

Каждая из этих задач работает правильно отдельно.

Но я хочу запустить некоторые из таких задач параллельно.Для этого я использую следующий код:

        public async Task<List<List<Data>>> GetAllDataAsync()
        {
            var resultTasks = new ConcurrentBag<Task<List<Data>>>();

            var firtsTask = GetDataWithTimeoutAsync(firstInputData, firtsTimeout);
            var secondTask = GetDataWithTimeoutAsync(secondInputData, secondTimeout);
            var thirdTask = GetDataWithTimeoutAsync(thirdInputData, thirdTimeout);

            resultTasks.Add(Task.Run(() => firtsTask));
            resultTasks.Add(Task.Run(() => secondTask));
            resultTasks.Add(Task.Run(() => thirdTask));

            await Task.WhenAll(resultTasks);

            var result = resultTasks.Select(t => t.Result).ToList();

            return result;

        }

Но этот код работает неправильно, если для вложенных задач установлены разные таймауты.В этом случае все задачи завершаются после наименьшего времени ожидания.

Как я могу запускать некоторые задачи параллельно с WhenAll, если каждая задача является результатом WhenAny?

1 Ответ

0 голосов
/ 24 апреля 2019

Ваш код не компилируется, поэтому я написал нечто подобное.Я не могу воспроизвести ваши результаты.В моем случае WhenAll с разными таймаутами работает как положено.Он завершается после завершения самой продолжительной задачи, которая является второй (200 мс).

public static async Task Main(string[] args)
{
    var task1 = GetDataAsync(100).WithTimeout(50);  // Should timeout after 50 msec
    var task2 = GetDataAsync(200).WithTimeout(300); // Should complete after 200 msec
    var task3 = GetDataAsync(300).WithTimeout(100); // Should timeout after 100 msec
    var stopwatch = Stopwatch.StartNew();
    var results = await Task.WhenAll(task1, task2, task3); // Wait for all
    stopwatch.Stop();
    Console.WriteLine($"Results: {String.Join(", ", results)}");
    Console.WriteLine($"Elapsed: {stopwatch.ElapsedMilliseconds} msec");
}

private static async Task<int> GetDataAsync(int input) // the input is used as delay
{
    await Task.Delay(input);
    return input;
}

public static Task<T> WithTimeout<T>(this Task<T> task, int timeout)
{
    var delayTask = Task.Delay(timeout).ContinueWith(_ => default(T),
        TaskContinuationOptions.ExecuteSynchronously);
    return Task.WhenAny(task, delayTask).Unwrap();
}

Вывод:

Results: 0, 200, 0
Elapsed: 211 msec
...