Parallel start of several tasks, each of which has

2019-07-25 11:08发布

问题:

I have several similar tasks, each of which is limited to a timeout, i.e. must be completed faster than the specified time or return an empty result. The main purpose of these tasks is receiving a response from the server with a timeout limit. An example of such task is below:

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

Each of these tasks works correctly separately.

But I want to run some of such tasks in parallel. For this, I use the following code.

        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;

        }

But this code works incorrectly if different timeouts are set for nested tasks. In this case all of tasks are completed after the smallest of the timeouts.

How i can run some tasks in parallel with WhenAll if each of task is a result of WhenAny?

回答1:

Your code doesn't compile, so I wrote something similar. I can't reproduce your results. In my case the WhenAll with different timeouts works as expected. It completes when the longest running task is completed, which is the second one (200 msec).

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

Output:

Results: 0, 200, 0
Elapsed: 211 msec