Awaiting multiple Tasks with different results
After you use WhenAll
, you can pull the results out individually with await
:
var catTask = FeedCat();
var houseTask = SellHouse();
var carTask = BuyCar();
await Task.WhenAll(catTask, houseTask, carTask);
var cat = await catTask;
var house = await houseTask;
var car = await carTask;
You can also use Task.Result
(since you know by this point they have all completed successfully). However, I recommend using await
because it's clearly correct, while Result
can cause problems in other scenarios.
await multiple tasks and access results
You are iterating (on the foreach
) to a Task
variable (which doesn't have a typed Result
), you need to iterate to a Task<Service>
variable (or use var
, since your tasks
list is already typed), that is:
foreach (var t in tasks)
{
services.Add(t.Result);
}
How to run two lists of tasks with different return types at the same time
Note that Task<T>
actually inherits from the non-generic Task
so you cast all your generic tasks to the base class, something like this:
var nonGenericTasks = catTasks
.Cast<Task>()
.Concat(dogTasks.Cast<Task>());
await Task.WhenAll(nonGenericTasks);
Collecting results of async within lambda
Another way to do it is to project each long
ID to a Task<ValueTuple<long, bool>>
, instead of projecting it to a Task<bool>
. This way you'll be able to filter the results using pure LINQ:
private async Task<long[]> GetValidIds3(long[] ids)
{
IEnumerable<Task<(long Id, bool IsValid)>> tasks = ids
.Select(async id =>
{
bool isValid = await CheckValidIdAsync(id).ConfigureAwait(false);
return (id, isValid);
});
var results = await Task.WhenAll(tasks).ConfigureAwait(false);
return results
.Where(e => e.IsValid)
.Select(e => e.Id)
.ToArray();
}
The above GetValidIds3
is equivalent with the GetValidIds1
in your question. It returns the filtered IDs in the same order as the original ids
. On the contrary the GetValidIds2
doesn't guarantee any order. If you have to use a concurrent collection, it's better to use a ConcurrentQueue<T>
instead of a ConcurrentBag<T>
, because the former preserves the insertion order. Even if the order is not important, preserving it makes the debugging easier.
How to pass additional context information along with tasks to Task.WhenAll?
You can either do the zipping as you go:
public async Task<IEnumerable<(string, SomeObject)>>
DoManyThingsAsync(IEnumerable<string> someListOfStrings, bool condition)
{
var tasks = someListOfStrings
.Select(async item =>
condition ?
(item, await DoSomethingAsync(item)) :
(item, await DoSomethingElseAsync(item)))
.ToList();
return await Task.WhenAll(tasks);
}
Or, you can keep the input as a separate collection and zip it later:
public async Task<IEnumerable<(string, SomeObject)>>
DoManyThingsAsync(IEnumerable<string> someListOfStrings, bool condition)
{
// Reify input so we don't enumerate twice.
var input = someListOfStrings.ToList();
var tasks = input
.Select(item =>
condition ?
DoSomethingAsync(item) :
DoSomethingElseAsync(item))
.ToList();
var taskResults = await Task.WhenAll(tasks);
return input.Zip(taskResults, (item, taskResult) => ((item, taskResult)));
}
Waiting multiple Tasks on .NET
Once you have successfully awaited Task.WhenAll
, you know that the tasks are completed, so you can use task1.Result
, task2.Result
, etc. to get the individual task results.
Related Topics
Difference Between Asynchronous Programming and Multithreading
Creating Wizards For Windows Forms in C#
How to Perform a Left Outer Join Using Linq Extension Methods
Replace Line Breaks in a String C#
Get String Between Two Strings in a String
Getting Servicestack to Retain Type Information
Linq-To-Entities Join VS Groupjoin
Sending Http Requests in C# With Unity
How to Get Children of a Wpf Container by Type
If Async-Await Doesn't Create Any Additional Threads, Then How Does It Make Applications Responsive
Unsubscribe Anonymous Method in C#
How to Cast Object of Type 'System.Dbnull' to Type 'System.String'
Check If a Class Is Derived from a Generic Class
What Does Principal End of an Association Means in 1:1 Relationship in Entity Framework