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 or Task 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;
}

References

http://channel9.msdn.com/Series/Three-Essential-Tips-for-Async/Tip-1-Async-void-is-for-top-level-event-handlers-only