Reputation: 93
I am studying asynchronous programming from Microsoft documentation and I don't understand the following example which uses the WhenAny
method:
var breakfastTasks = new List<Task> { eggsTask, baconTask, toastTask };
while (breakfastTasks.Count > 0)
{
Task finishedTask = await Task.WhenAny(breakfastTasks);
if (finishedTask == eggsTask)
{
Console.WriteLine("Eggs are ready");
}
else if (finishedTask == baconTask)
{
Console.WriteLine("Bacon is ready");
}
else if (finishedTask == toastTask)
{
Console.WriteLine("Toast is ready");
}
await finishedTask;
breakfastTasks.Remove(finishedTask);
}
You can find it here.
The problem with this code is that eggsTask
refers to a Task<Egg>
object, baconTask
refers to a Task<Bacon>
object, and toastTask
refers to a Task<Toast>
object.
Then he puts these 3 variables to a List
which then is an argument in Task.WhenAny(breakfastTasks)
method.
Now, I believe that the non-generic overload of WhenAny
is used, which you can see here. Specifically I believe this overload is used:
public static System.Threading.Tasks.Task<System.Threading.Tasks.Task> WhenAny(System.Collections.Generic.IEnumerable<System.Threading.Tasks.Task> tasks);
As you can see the parameter accepts IEnumerable<Task>
. Also this overload returns a Task<Task>
object, which when completed becomes a Task
object. So in this method you put in Task
objects and when one is completed, the method returns a Task
object which is the one Task
object that completed first.
However, when a Task<Egg>
object completes, it becomes an Egg
object, when a Task<Bacon>
object completes, it becomes a Bacon
object, and when a Task<Toast>
object completes, it becomes a Toast
object.
So, the line below doesn't seem right:
Task finishedTask = await Task.WhenAny(breakfastTasks);
Suppose eggsTask completes first, it gives an Egg
object which is supposed to be returned by the method, but the method when completed returns only a Task
object.
Could you please explain?
Upvotes: 0
Views: 88
Reputation: 19555
The overload of Task.WhenAny()
you are using has a return type of Task<Task>
. In combination with the await
keyword you will get a Task
reference that finished from the given list of tasks to observe.
This returned Task
reference is referencing one of your existing task references eggsTask
(Task<Egg>
), baconTask
(Task<Bacon>
) or toastTask
(Task<Toast>
). It does not return any Egg
, Bacon
or Toast
reference. To get the result from the finished task you still have to call await
on the Task<...>
instance. As an example, you could wrote
if (finishedTask == eggsTask)
{
Console.WriteLine("Eggs are ready");
Egg egg = await eggsTask;
}
to get the Egg
reference from that finished task. And since the task is already finished, this await
call will immediately return the Egg
reference (and save it in the egg
variable).
Keep in mind that Task<TResult>
is extending from Task
. So all your Task<Egg>
, Task<Bacon>
and Task<Toast>
references could be saved in a variable of type Task
. This is happening in the line
Task finishedTask = await Task.WhenAny(breakfastTasks);
However, this will "lose" the ability to get the result from this variable, as there is no generic type anymore. But as long as you have the original task references, you can get the results from these variables instead (eggsTask
, baconTask
and toastTask
) as shown above.
Upvotes: 4