Software Development

Why .NET developers should check out the await system

The new await keyword in the Async CTP makes it easier to write asynchronous code, so processes that can take up time no longer jam up the application's UI.

Jason Rainwater, a C# and WPF expert, spoke at the Columbia Enterprise Developer Guild meeting in early February about asynchronous programming in .NET. He gave a good history of asynchronous programming in .NET, and showed off a class called WorkerQueue that makes it pretty easy to asynchronously run code that interacts with the UI thread (historically, this has been an annoyance). He finished his presentation with something that I've been paying attention to lately: the new await keyword in the Async CTP and scheduled for C# 5.

The new await system is interesting because it allows you to run code that does not block the main UI thread, but at the same time can update the main UI thread. For anyone who has messed around with systems like BackgroundWorker, you know this is a real headache. And because the structure is built on Tasks (from .NET 4's Parallel Extensions Library), you get access to built-in functionality such as cancellation.

It's pretty easy to use the await system. If you want a method to run asynchronously, you mark it with the async keyword after its scope declaration, and you make at least one call within it using the await keyword. When you use await, it must be to a method that supports it. Out of the box, you get a couple of simple methods (such as WebClient.DownloadStringAsync) that support await. The method that is asynchronous can return void, Task, or Task<TResult>. If you choose to return a result using Task<TResult>, the compiler will do it automatically for you.

Here's a code sample:

private async void AsyncMethod() {

mainWindow.Control.Property = await GetDataAsync();

}

This code assumes you've made a GetDataAsync() method that supports await. When the await call is run, it checks to see if the GetDataAsync method has a result yet (remember, it uses Task under the hood). If the GetDataAsync method has a result, it returns it and keeps moving. If the GetDataAsync method doesn't have a result, it essentially allows control to return to the UI thread, keeping the application responsive, while the work is performed in a separate thread. There is a lot more to the details than what I've summarized, but it's enough to know that your application can keep running.

Something very important to keep in mind is that all of the code you have written runs in the UI thread, but it gets called via a "continuation" by the Task when it completes. Essentially, this is a good replacement for BackgroundWorker for certain scenarios.

The pattern's value

The pattern makes it easier to write asynchronous code, so processes that can take up time no longer jam up the application's UI. In the past, you needed to either home brew something with delegates or Thread, use BackgroundWorker (which was not a pretty pattern), or work with Tasks or lambdas. None of those options addressed the issue of UI updates, which required the BeginInvoke system because you can't modify data in one thread from another. In this pattern, all of your code is running in the UI thread, so you don't have the issues with UI updates. In addition, none of your code is running concurrently, which eliminates the data integrity issues (deadlocks, race conditions) typically associated with concurrency.

Caution: Because all of your code is running on the UI thread, if your async method takes up a lot of time outside of the await-ed call, you lose your benefits pretty quickly. Keep your work in the async method as minimal as possible, and the UI will show no slowdowns.

Summary

If you are doing things that are CPU bound, you should use proper parallel processing; this is not a replacement for that approach. But if you have something that is I/O bound, await is a great way to handle it.

I look forward to getting to work with await in a final form, and I think that it will be a great benefit to .NET developers.

J.Ja

About

Justin James is the Lead Architect for Conigent.

5 comments
conceptual
conceptual

I learned my lesson about CTPs with Office 2010. I had terrible support, bad advice when problems arose. When the problem couldn't be fixed it was marked resolved when it wasn't. The removal software failed to remove the beta, so did following their advice to remove all versions of office via add/remove. I have a laptop with no working version of Microsoft Office and no version of Office will install because the software thinks office is already on the machine. Call me when version 3.1 is ready.

bluemoonsailor
bluemoonsailor

What's the real benefit of this? You're basically stealing cycles from the UI thread - which you don't want to do. That's why you're calling an asynchronous process. The "Caution" mentioned above is very real, and the fundamental reason why you shouldn't be doing this in the first place.

Justin James
Justin James

I agree about Office and Windows CTPs and betas. I won't touch 'em except for research purposes (as in, "what new features are here?"). Even then, they are of poor quality much of the time. CTPs from Microsoft's .NET group, though, tend to be of excellent quality. The Parallel Extensions Library, for example, was real-world usable in 2008... two years before it got a version 1 release. Ditto for F#... what came out in .NET 4 was F# 2, I believe! In general, people seem happy with ASP.NET MVC CTPs and betas and such. J.Ja

Justin James
Justin James

Stealing cycles from the UI thread isn't a big deal for the scenarios that await is designed for. For example, downloading a file, or waiting for a DB connection to return results. I agree 100% that it's a bad idea for CPU bound items, in which case you are recreating the problem that asynchronous operations are trying to avoid (responsive UI). Also, when you get to Silverlight, async stuff is mandatory in a lot of circumstances, so await is a much easier way of dealing with it. Await is much more useful to the Silverlight developer (or WPF developer trying to keep their code SL compatible) than the WinForms developer. J.Ja

ps2goat
ps2goat

The point that Justin was making with await vs. the Background worker is that control is passed to the UI automatically while the function called with await finishes. A classic example is a waiting for things to load, e.g., let's say you're searching for something that takes a long time to retrieve. While the data is being searched for and loaded, await lets the thread return to the UI without having to use the background worker or start up a new thread, create a callback, and then have to remember to call the BeginInvoke on whatever control you want to update (e.g., a listbox) to prevent cross-thread interaction. Having the thread return to the UI and auto-creating a second thread makes for easier code and allows the UI to remain responsive-- the user could cancel the search, close the program, or use some secondary function while waiting, all without having to get the annoying 'Program is not responding' message.