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:
async
does not equal multiple threads; and- making a function
async Task
does not make it asynchronous
When Task.WhenAll
is run with pretend async code that has no await
s 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
Sort a List from Another List Ids
How to Enable C# 6.0 Feature in Visual Studio 2013
Concatenate and Minify JavaScript on the Fly or at Build Time - ASP.NET MVC
Conditionally Change CSS Class in Razor View
How to Modify a CSS Style in the Code Behind File for Divs in ASP.NET
How to Spawn a Process and Capture Its Stdout in .Net
Given a Filesystem Path, Is There a Shorter Way to Extract the Filename Without Its Extension
Should I Avoid 'Async Void' Event Handlers
Creating Wizards For Windows Forms in C#
How Should the Viewmodel Close the Form
Covariance and Contravariance Real World Example
Enum Tostring With User Friendly Strings
How to Bind to a Passwordbox in Mvvm
Deciding Between Httpclient and Webclient
Mvc Form Not Able to Post List of Objects
Most Efficient Way to Randomly "Sort" (Shuffle) a List of Integers in C#
C# Error: "An Object Reference Is Required For the Non-Static Field, Method, or Property"