Async Tips for .NET
Recently I made a free Windows 8 Application, Pixel Hero, and as part of this process I needed to use file and image APIS throughout the app. These APIS are modern C# 4.5 APIs so they expose Async methods throughout. You can get very far with Async by following a small set of rules.
MSDN image showing how Async works... simple enough:)
Rules of Thumb
async void
method signatures are only for event handlers- use
async Task
for all other methods other than event handlers (when you want async functionality) - for IO bound work, use await rather than background threads versus for CPU bound work, use Parallel.For or Task.Run
- when you see an async lambda verify it is a
void
orTask
returning lambda
Async Void
A method marked with async void
has some unique characteristics. Methods marked with async void
act as "fire-and-forget" mechanism. Said in another way, the caller is unable to know when an async void
method has finished. The caller cannot catch exceptions from an async void
method. Example usage of async void
shown below.
private async void Button_Click
Problem Scenarios
In certain cases you are not allowed to use async methods. Async is not allowed in constructors or property getters.
Can't use await in constructors? - use an async factory method instead.
Can't use await in property getters? - make it a method rather than a property.
Async event handlers are re-entrant? - temporarily disable UI
Windows 8 Metro App Tip
A common issue with Windows 8 Metro apps is not realizing that LoadState is called by OnNavigated. People will make LoadState async void
because they follow the pattern that event handlers should be async void
. We cannot do our normal technique of returning Task for LoadState as it is called by the base method of OnNavigated.
// Bad Code...
BitmapImage m_bmp;
protected override async void OnNavigated(NavigationEventArgs e) {
base.OnNavigatedTo(e); // Calls this.LoadState
...
image.Source = m_bmp;
}
protected override async void LoadState(Object nav, Dictionary...) {
...
await m_bmp.SetSourceAsync(stream);
...
}
// Good... fixes race condition of loading image in above code
Task<BitmapImage> _bmpTask;
protected override async void OnNavigated(NavigationEventArgs e) {
base.OnNavigatedTo(e); // Calls this.LoadState
...
var bmp = await _bmpTask;
image.Source = bmp;
}
protected override void LoadState(Object nav, Dictionary...) {
...
_bmpTask = LoadBitmapAsync();
...
}
private asnyc Task<BitmapImage> LoadBitmapAsync() {
return bmp;
}