async/await, SynchronizationContext tips.
I've been using async/await for some time now and I do find it very clean and tidy compared to pure TPL approach. But there are a few caveats that I found quite annoyingly hard to remember, or rather these things were not readily discoverable.
And now at my n-th iteration to dive into async world of WinRT with C# I will record in concise form some of the tips for future reference.
Example:
public async void PointerPressedAsync( object sender, PointerRoutedEventArgs e ) { // top-level async operation // 1. context is captured // 2. async context timer is incremented // 3. initiate await for long-running tasks // 4. async context timer is decremented // 5. exit method, but context is still captured // 6. schedule callbacks in the same way as for all other types of async methods. }
1. SynchronizationContext flavors
These are classes with instances that help align asynchronous tasks back to original threads of code. To clarify, async long-running tasks usually end with callbacks to original code, but not exactly at the point of task invocation. Callbacks are serialized with the rest of original code to run in the same thread (usually for UI). If thread is occupied, the context will schedule all outstanding callbacks to run in FIFO queue order. And this is the case with classic UI applications (WinForms, WPF, WinRT), but a bit different for WCF and ASP.NET.
More about SynchronizationContext flavors is here.
2. Asynchronous operation count
This is maintained by SynchronizationContext instance and keeps track of outstanding async operations. This is done by a relatively simple increment at the moment the context is captured (awaiting for an async task), and decrement when callback is scheduled at the task completion.
3. async void vs async Task
These two methods capture external context in the same way and schedule their inner async callbacks (state machine states) to run in those contexts, unless specified otherwise (by using ConfigureAwait(false)).
The difference is that async void will increment/decrement asynchronous operation count (see above) at the method's start/end, that is while its initial async state runs. This is useful when application code does not really need to keep track of exact moment when async operation completes.
These async void operations are also known as top-level, not requiring outer caller methods to be declared async. In fact the only possible async signature for the top-level user code event handlers is async void.
This works well with other legacy libraries or external code that accepts event handlers. In this case client code with async callbacks can replace void with async void.
Note that top-level method needs to take care about catching all exceptions from awaited tasks.
More great insights into one async vs another are in MSDN Mag forum and in Jerome Laban's blog.
"Diving deep with WinRT and await" by Stephen Toub













