Reputation:
The best I've been able to find in my research for a "do something with the results of tasks in a list as they complete" is this Microsoft page Process asynchronous tasks as they complete, unfortunately the example within accumulates the results of the tasks which I don't need to do; I only want to: spawn list of tasks, when a task completes, do something with its result.
So here I have adapted their example to demonstrate what I mean:
static async Task SumPageSizesAsync()
{
IEnumerable<Task<int>> downloadTasksQuery =
from url in s_urlList
select ProcessUrlAsync(url, s_client);
List<Task<int>> downloadTasks = downloadTasksQuery.ToList();
while (downloadTasks.Any())
{
Task<int> finishedTask = await Task.WhenAny(downloadTasks);
downloadTasks.Remove(finishedTask);
Console.WriteLine($"Just downloaded ${finishedTask} bytes");
}
}
After any download task finishes, print an alert that it has finished (for example).
How can I more succinctly accomplish the same thing?
Is
downloadTasksQuery.Select(async t => Console.WriteLine($"Just downloaded ${await t} bytes"));
a good solution?
Upvotes: 1
Views: 929
Reputation: 457217
research for a "do something with the results of tasks in a list as they complete" is this Microsoft page Process asynchronous tasks as they complete,
As I describe in my book (section 2.6 "Processing Tasks as They Complete"), this is usually the wrong thing to search for.
Instead of thinking about the problem that way, compose the problem at a different level. Don't think about it as:
Instead, compose A and B together, and then you have:
The resulting code is much cleaner:
static async Task SumPageSizesAsync()
{
// Composition is done with async+await
var tasks = s_urlList.Select(async url =>
{
var result = await ProcessUrlAsync(url, s_client);
Console.WriteLine($"Just downloaded {result} bytes");
}).ToList();
// Asynchronous concurrency is done with Select + Task.WhenAll
await Task.WhenAll(tasks);
}
Upvotes: 1
Reputation: 43886
Yes, your approach is OK. Let's rewrite it in a less succinct way for clarity:
Task[] continuations = downloadTasksQuery
.Select(async task =>
{
var result = await task;
Console.WriteLine($"Just downloaded ${result} bytes")
})
.ToArray();
await Task.WhenAll(continuations);
The continuations will run on the current SynchronizationContext
, if one exists, or on an unknown context otherwise. Without knowing the context, we can't say for sure if the continuations will be serialized or not.
Upvotes: 1