Lukas Vencalek Lukas Vencalek - 27 days ago 9
C# Question

How to run all tasks in array and process the results continuously (async, await)?

I have following code:

List<Task<int>> taskArray = new List<Task<int>>();

for (int i = 0; i < 100; i++)
{
taskArray.Add(myCrawler.getWebPageCharsCount("https://www.something.com"));
}


The method looks like this:

public Task<int> getWebPageCharCount(string url)
{
var client = new HttpClient();

return Task.Run(async () =>
{
Task<string> task = client.GetStringAsync(url);
string taskResult = await task;
return taskResult.Length;
});
}


After this, there is a 100 threads running, what I want to achive is to process the result in main thread after each individual task is done, not to wait to all results, which would I do with the following code:

var results = await Task.WhenAll(taskArray);

foreach (var res in results)
{
myTextBox.Text += res.ToString() + "\n";
}


I was thinking about something like this:

foreach (var task in taskArray)
{
var result = await task;
myTextBox.Text += result.ToString() + "\n";
}


But after testing and reading about await in loops, I know it's running synchronized. Is there a way to process the results continuously in main thread?

Answer

There are several solutions to this, but the simplest is to use await Task.WhenAny(), which returns a completed Task, which you then process and remove from the list of tasks, then repeat the same until the list is empty.

Example as requested:

List<Task<int>> indexingArray = new List<Task<int>>(taskArray);
var results = new int[taskArray.Count];

while (taskArray.Any())
{
    Task<int> completedTask = await Task.WhenAny(taskArray).ConfigureAwait(false);
    results[indexingArray.IndexOf(completedTask)] = completedTask.Result;
    taskArray.Remove(completedTask);
}

myTextBox.Text = string.Join("\n", results);

(I added an indexing collection to get the correct index for the results. There are probably better ways to do that but as the question is about tasks I'll leave it as-is).