Running Multiple Async Tasks and Waiting For Them All to Complete

Running multiple async tasks and waiting for them all to complete

Both answers didn't mention the awaitable Task.WhenAll:

var task1 = DoWorkAsync();
var task2 = DoMoreWorkAsync();

await Task.WhenAll(task1, task2);

The main difference between Task.WaitAll and Task.WhenAll is that the former will block (similar to using Wait on a single task) while the latter will not and can be awaited, yielding control back to the caller until all tasks finish.

More so, exception handling differs:

Task.WaitAll:

At least one of the Task instances was canceled -or- an exception was thrown during the execution of at least one of the Task instances. If a task was canceled, the AggregateException contains an OperationCanceledException in its InnerExceptions collection.

Task.WhenAll:

If any of the supplied tasks completes in a faulted state, the returned task will also complete in a Faulted state, where its exceptions will contain the aggregation of the set of unwrapped exceptions from each of the supplied tasks.

If none of the supplied tasks faulted but at least one of them was canceled, the returned task will end in the Canceled state.

If none of the tasks faulted and none of the tasks were canceled, the resulting task will end in the RanToCompletion state.
If the supplied array/enumerable contains no tasks, the returned task will immediately transition to a RanToCompletion state before it's returned to the caller.


How to best run two async tasks in parallel and wait for the returned results of both?

You can use the async and await pattern and await the cpu bound tasks, however you will need to use async Task Main() entry point

public static async Task Main()
{
var task1 = Task.Run(() => CpuWork1(1));
var task2 = Task.Run(() => CpuWork2(10));

var result1 = await task1;
var result2 = await task2;

// or

var results = await Task.WhenAll(task1, task2) ;
}

If your workloads are IO bound, then they would have the async Task<T> signature, and you would just await the methods returned tasks, similar to above (and not use Task.Run)

Complete Example

static async Task Main()
{

var stopWatch = new Stopwatch();
stopWatch.Start();

var task1 = Task.Run(() => DoSomeWork(1));
var task2 = Task.Run(() => DoSomeOtherWork(10));

var results = await Task.WhenAll(task1, task2);
stopWatch.Stop();

Debug.WriteLine($"Sum: {results.Sum()}");
Debug.WriteLine($"Final: {stopWatch.Elapsed}");
}

Or similarly with an async methods

static async Task Main()
{

var stopWatch = new Stopwatch();
stopWatch.Start();

var task1 = DoSomeWorkAsync(1);
var task2 = DoSomeOtherWorkAsync(10);

var results = await Task.WhenAll(task1, task2);
stopWatch.Stop();

Debug.WriteLine($"Sum: {results.Sum()}");
Debug.WriteLine($"Final: {stopWatch.Elapsed}");
}

private static async Task<int> DoSomeOtherWorkAsync(int waitFor)
{
// some IO bound workload
await Task.Delay(waitFor * 1000);
return waitFor;
}

private static async Task<int> DoSomeWorkAsync(int waitFor)
{
// some IO bound workload
await Task.Delay(waitFor * 1000);
return waitFor;
}

how to wait for multiple async tasks in c#?

You don't want to use Task.Factory.StartNew (nor would the better Task.Run needed either), and you shouldn't use Task.WaitAll for asynchronous code.

Instead, ensure the method is marked as async, then just start the tasks, without awaiting, and then use await the result of Task.WhenAll

 var task1 = table1.ExecuteQueryAsync(query);
var task2 = table2.ExecuteQueryAsync(query2);

await Task.WhenAll(task1, task2);

// Access the results of the completed Tasks here

Fire multiple async tasks and only update result from latest task

I use what I call an "asynchronous context" for this kind of scenario (probably a really bad name, given "context" can mean so many things these days). You just need some kind of unique value (new object() is sufficient), capture this value into a local variable before the await, and then compare it after the await. If they don't match, then that code knows it's no longer the current one.

private object _context;

protected async Task OnSearchTermChanged(ChangeEventArgs e)
{
SearchTerm = e.Value.ToString();

if (SearchTerm.Length >= 3)
{
var localContext = _context = new object();
SearchResult = null;
var task = AdService.SearchUsers(SearchTerm);
var result = await task;

if (localContext == _context)
{
SearchResult = result;
}
}
else
{
_context = null;
}
}

Waiting for multiple synchronous tasks to finish

I think you could use Task.WaitAll() after running the tasks and before returning from Export function.

public string Export()
{
var ifcFilePaths = ExportIfcFiles().ToList();
Task<List<IfcToCsvGraphExportResult>> graphConverterResults = ConvertIfcToGraphData(ifcFilePaths);
Task<List<IfcToGltfConversionResult>> gltfConverterResults = ConvertIfcToGltfData(ifcFilePaths);

// This is the missing part
Task.WaitAll(new[] {graphConverterResults, gltfConverterResults});

List<string> folders = _outputFolderPacker.Pack(graphConverterResults.Result, gltfConverterResults.Result).ToList();

return _zipPacker.Pack(folders);
}

Some detail can be found on Microsoft docs page: https://docs.microsoft.com/en-us/dotnet/api/system.threading.tasks.task.waitall?view=net-5.0

How to parallel process multiple async task foreach loop

Since you are not awaiting the call to GetIsConnected() in your AFTER example, the loop will continue and the value may or may not be checked against what it would be once the call to GetIsConnected() is finished, what I think you'd want to do is add another method with a return type of Task and let the method call GetIsConnected() and await it inside the method then call GetCarDetailsAsync() if necessary:

//changed to return type of Task, as async void should be avoided
//unless this is an event handler method
private async Task Refresh()
//private async void Refresh() //change to this if event handler
{
List<Task> listOfTasks = new List<Task>();
foreach (var item in Cars.ToList<Carnet>())
{
listOfTasks.Add(GetCarDetails(item));
}
await Task.WhenAll(listOfTasks).ConfigureAwait(false);
}

//method to await the call to GetIsConnect()
//and then call GetCarDetailsAsync() if necessary
private async Task GetCarDetails(Carnet c)
{
await c.GetIsConnected();
if (c.IsConnected)
{
await c.GetCarDetailsAsync();
}
}

Trying to run multiple tasks in parallel with .WhenAll but tasks aren't being run in parallel

In short:

  1. async does not equal multiple threads; and
  2. making a function async Task does not make it asynchronous

When Task.WhenAll is run with pretend async code that has no awaits the current thread cannot 'let go' of the task at hand and it cannot start processing another task.

As it was pointed out in the comments, the build chain warns you about it with:
This async method lacks 'await' operators and will run synchronously. Consider using the 'await' operator to await non-blocking API calls, or 'await Task.Run(...)' to do CPU-bound work on a background thread.

Trivial example

Let's consider two function with identical signatures, one with async code and one without.

static async Task DoWorkPretendAsync(int taskId)
{
Console.WriteLine($"Thread: {Thread.CurrentThread.ManagedThreadId} -> task:{taskId} > start");
Thread.Sleep(TimeSpan.FromSeconds(1));
Console.WriteLine($"Thread: {Thread.CurrentThread.ManagedThreadId} -> task:{taskId} > done");
}

static async Task DoWorkAsync(int taskId)
{
Console.WriteLine($"Thread: {Thread.CurrentThread.ManagedThreadId} -> task:{taskId} > start");
await Task.Delay(TimeSpan.FromSeconds(1));
Console.WriteLine($"Thread: {Thread.CurrentThread.ManagedThreadId} -> task:{taskId} > done");
}

If we test them with the following snippet

await DoItAsync(DoWorkPretendAsync);
Console.WriteLine();
await DoItAsync(DoWorkAsync);

async Task DoItAsync(Func<int, Task> f)
{
var tasks = Enumerable.Range(start: 0, count: 3).Select(i => f(i));
Console.WriteLine("Before WhenAll");
await Task.WhenAll(tasks);
Console.WriteLine("After WhenAll");
}

we can see that with DoWorkPretendAsync the tasks are executed sequentially.

Before WhenAll
Thread: 1 -> task:0 > start
Thread: 1 -> task:0 > done
Thread: 1 -> task:1 > start
Thread: 1 -> task:1 > done
Thread: 1 -> task:2 > start
Thread: 1 -> task:2 > done
After WhenAll

Before WhenAll
Thread: 1 -> task:0 > start
Thread: 1 -> task:1 > start
Thread: 1 -> task:2 > start
Thread: 5 -> task:0 > done
Thread: 5 -> task:2 > done
Thread: 7 -> task:1 > done
After WhenAll

Things to note:

  • even with real async all tasks are started by the same thread;
  • in this particular run two of the task are finished by the same thread (id:5). This is not guaranteed at all - a task can be started on one thread and continue later on another thread in the pool.

run multiple task and await for all tasks completes

Check this out:

 var task1 = _repository.GetCountAsync<filter>(Predicate);
var task2 = _repository.GetCountAsync<Flight>(predicate.And(x => x.Status == "refused"));

await Task.WhenAll(task1, task2); //wait both tasks to finish

model.Alive = await task1;
model.Delay = await task2;

PS: the function, which the above code is placed, is needed to be async for the above scenario.

C# multiple async tasks and where to properly await them when they use each other to complete?

I think you misinterpret the flow of data.

Here are your two methods written a bit more verbose

Method 1

var renderTask = RenderDocumentBuilder.Instance.GetRenderDocumentDirectiveAsync(previousPage, session);

var renderDocumentDirective = await renderTask;

var alexaResponse = new Response();

alexaResponse.shouldEndSession = null,
alexaResponse.directives = new List<IDirective>();
alexaResponse.directives.Add(renderDocumentDirective);

var buildTask = ResponseClient.Instance.BuildAlexaResponse(alexaResponse, session.alexaSessionDisplayType);

return await buildTask;

Method 2

var renderTask = RenderDocumentBuilder.Instance.GetRenderDocumentDirectiveAsync(previousPage, session);

var alexaResponse = new Response()
alexaResponse.shouldEndSession = null,
alexaResponse.directives = new List<IDirective>();
alexaResponse.directives.Add(await renderTask);

var buildTask = ResponseClient.Instance.BuildAlexaResponse(alexaResponse, session.alexaSessionDisplayType);

return await buildTask;

So you see that the only real difference is that methode 2 creates the Response object, sets shouldEndSession and creates the List object before or it awaits the renderTask.

Method 2 might be beneficial, but this depends on how GetRenderDocumentDirectiveAsync is implemented (i.e. truely async). But even if it is, it is highly unlikly that method 2 brings any performance gains as there is not much difference between both methods.

That said, I would go with method 1, because it looks more like sync code and in most cases you want to await a Task as soon you have it available, because await/async is mainly about freeing threads to do other stuff and not about parallalism.



Related Topics



Leave a reply



Submit