I have the following code:
List<Task<bool>> tasks = tasksQuery.ToList();
while (tasks.Any())
{
Task<bool> completedTask = await Task.WhenAny(tasks);
if (await completedTask)
return true;
tasks.Remove(completedTask);
}
It launches tasks in parallel. When first completed task returns true the methods returns true.
My question is:
What happens with all remaining tasks that have been launched and probably still running in the background?
Is this the right approach to execute a code that is async, parallel and should return after the first condition occurs or it is better to launch them one by one and await singularly?
Thanks
Incidentally, I am just reading Concurrency in C# CookBook, by Stephen Cleary, and I can refer to some parts of the book here, I guess.
From Recipe 2.5 - Discussion, we have
When the first task completes, consider whether to cancel the remaining tasks. If the other tasks are not canceled but are also never awaited, then they are abandoned. Abandoned tasks will run to completion, and their results will be ignored. Any exceptions from those abandoned tasks will also be ignored.
Another antipattern for Task.WhenAny is handling tasks as they complete. At first it seems like a reasonable approach to keep a list of tasks and remove each task from the list as it completes. The problem with this approach is that it executes in O(N^2) time, when an O(N) algorithm exists.
Besides that, I think WhenAny
is surely the right approach, just consider following Leonid approach of passing the same CancellationToken
to every task and cancel them after the first one returns. And even though, only in case the cost of these operations are actually taxing the system.