Some explanation as I understand it.
If you did this:
//some code
await Task1Async();
await Task2ASync();
//some other code
It would execute like this:
- Calling thread (eg UI context) executes "some code" synchronously.
- It then launches Task1Async asynchronously which will happen on the ThreadPool context,( unless it is already complete in which case execution continues at point 4 below synchronously). The calling context is capture here if the task is not complete
- The current thread then returns from the function, meaning it is not blocked.
- When Task1Async is complete, the next line is executed in the captured context eg the UI context. This then launches Task2Async asynchronously via the threadpool context (again if already complete, execution continues synchronously)
- Task2Async completes and then the finally "some other code" is run again using the captured context
If you instead do this:
//some code
await Task1Async().ConfigureAwait(false);
await Task2ASync().ConfigureAwait(false);
//some other code
This is what happens:
1. Calling thread executes "some code".
2. Then executes Task1Async asynchronously (threadpool).
3. The current thread returns from the function, meaning it is not blocked.
4. When Task1Async is complete, the next line is executed NOT on the captured context but instead using the threadpool. This then launches Task2Async.
5. Task2Async completes and then the final "some other code" is run again not on the calling thread but instead using the threadpool.
In both cases the functions run sequentially but do not block the calling thread.
With Task.WaitAll, the two tasks are run sequentially using the threadpool. I.e. each task is launched in a separate thread and waited for completion. The major difference here is that the calling thread is blocked until both tasks are complete.
If you want to run the two tasks in parallel and not block the calling the thread you can await WhenAll:
await Task.WhenAll(Task1Async, Task2Async);
And finally if you have any code after the await that you want to run that does not need to be run in the UI context then you would do:
await Task.WhenAll(Task1Async, Task2Async).ConfigureAwait(false);
Side note: the reason you would want ConfigureAwait(true) or omit it is if you had for example a button click handler and wanted to update something that needed to be on the UI thread:
public void OnClick(..args..)
{
Button.IsEnabled = false;
await SomeTask();
//must be on UI thread. This code is executed on the context captured by the await above.
Button.IsEnabled = true;
}