Asynchronous Processing in Windows 8
November 7, 2011
New York, N.Y.
A typical book about Windows 8 programming is likely to be rather oddly structured: It may start out like conventional programming tutorials with "hello world" but very early on the book will likely devote a chapter to a topic commonly relegated to the advanced techniques section: "Asynchronous Processing".
This blog entry is not that chapter! My coverage here is very superficial, omits a number of details and variations, and might even contain a couple errors. (Like most people outside Microsoft, I am very new to Windows 8 programming.) But I need to show you some of this stuff so my next blog entry won't be a complete mystery.
Some background: From the very early days of Windows programming, application developers realized that they couldn't arbitrary execute a big chunk of lengthly code. As I wrote for the first edition of Programming Windows in 1987, "Windows is a nonpreemptive multitasking environment, and it is important that programs return control to Windows as quickly as possible." Otherwise a single program could hang the whole system. In those days, a program with a lengthy job had to split the job into pieces executed during a Timer tick, or use PeekMessage to periodically yield control back to the operating system.
Windows later got real multitasking and multithreading, but this issue didn't go away. Even if a single program couldn't hang the whole system — at least in theory — it could still hang itself and become unresponsive to user input. Programs that wished to do lengthy processing jobs could spin off secondary threads and execute those jobs in the background.
However, Windows programs that create secondary threads can't do so with complete freedom. For any particular window, there can be only one application thread that handles user input, including keyboard, mouse, stylus, and touch input, and displaying graphics that interact with this input. This "UI thread" (as it's called) is consequently very important and very special to Windows applications because all interaction with the user must occur through this thread. For this reason, Windows frameworks that allow programs to spin off secondary threads also include a facility for secondary threads to queue code for running on the UI thread. In Windows Forms, Control defines a BeginInvoke method for this purpose. In WPF, Silverlight, and Windows Phone, every UI object has a Dispatcher property, which is an object of type Dispatcher with a BeginInvoke method.
Enter Windows 8, the most significant updating of the Windows UI and API since the inception of Windows in 1985. The UI of Windows 8 is intended to be "fast and fluid" (to quote the primary buzz phrase heard at September's Build conference). An unresponsive Windows 8 program is a broken program.
Imagine if someone told you that your application was prohibited from executing any code on the UI thread that required more than 50 milliseconds to complete. If you had any such code in your application, you were required to move that code to a secondary thread. You probably wouldn't be happy.
Well, this is the rule that the developers of Windows 8 imposed on themselves. The Windows Runtime (or WinRT) has a bunch of classes with a bunch of methods. A Windows 8 application needs to call these methods from its UI thread, and if these methods themselves are requiring long processing time, they would block the UI thread and cause the application to be unresponsive. It is not good for Windows 8 itself to be violating the "fast and fluid" mandate!
It turned out that about 10 to 15% of the method calls in WinRT could possibly require more than 50 milliseconds to return control back to the program. To prevent these methods from hanging a program, they were made asynchronous.
For example, consider a method named OpenFile. You give this method a file name, and the method opens that file for you and returns some kind of handle or stream. Because this function hits the disk, it might require more than 50 milliseconds to execute. This means it's a candidate for being turned into an asynchronous method.
The method is renamed to OpenFileAsync. (Every asynchronous method in Windows 8 ends with the word Async.) This method does not return a handle or stream to the open file. It returns an object sometimes called an "operation" or a "future" or a "promise" to open the file. Using this object, the application program specifies a callback method to be called when the open-file operation completes. The application then calls Start. At this point, the application program returns control back to Windows, unblocking the UI thread, and the OpenFileAsync method does its work. When it has the opened file ready, it calls the application's callback method, and the application now has a handle or stream to the open file.
Like I said, something like 10 to 15% of the API calls you'll use in a Windows 8 program are of this nature. For the application programmer, dealing with all these asynchronous methods obviously sounds like a royal PITA. However, it's been made remarkably easy, in part by standardizing and formalizing all the asynchronous method calls, but mostly by shifting a lot of the messy stuff to the compiler with two new C# 5.0 keywords await and async. (Similar keywords are added to Visual Basic.)
This stuff is actually not new with Windows 8. In October 2010, asynchronous processing based around the .NET Task<T> class because available in a Community Technology Preview (CTP), and recently version 3 of the Async CTP became available. This Async CTP is not what's been implemented in the Windows 8 Runtime library, but it is very similar, and much information is available about it, including The Task-Based Asynchronous Pattern, a white paper by my old friend and editor Stephen Toub, and three articles (including the cover story) in the October issue of MSDN Magazine by Eric Lippert, Mads Torgersen, and Stephen Toub. Again, let me emphasize that these articles are not about Windows 8 asynchronous methods, but they are likely to provide many insights regardless. Moreover, this asynchronous support is part of the subset of .NET that is part of Windows 8, so you can use these techniques in your Windows 8 programs.
Windows 8 can thus be said to support two models of asynchronous processing — one inherited from .NET and the other specific to the Windows Runtime. They are conceptually very similar, although a little different in detail.
For the Windows 8 approach to asynchronous processing, check out Asynchronous Patterns in the Windows Runtime in the official documentation, and some videos from Build: Async Everywhere: Creating Responsive APIs & Apps by Ben Kuhn, Async Made Simple in Windows 8, with C# and Visual Basic by Alex Turner and Mads Torgersen, a video particularly strong on the relationship between the .NET and WinRT mechanisms, and The Zen of Async: Best Practices for Best Performance by the ubiquitous Stephen Toub. Also recommended is the first half of Anders Hejlsberg's talk Future directions for C# and Visual Basic, but you might want to continue watching for a glimpse of Anders doing a bit of JavaScript programming! (I also love Anders' metaphor for a program that becomes unresponsive: it "frosts over.")
It's amazing how even in a tiny Windows 8 program you're likely to encounter asynchronous methods! Suppose you want to write a program with a button that invokes a file-open dialog that lets the user pick an image file from the hard drive. The program then loads in this image file and displays it using the Image element. Instead of a file-open dialog, WinRT includes a FileOpenPicker class in the Windows.Storage.Pickers namespace. You instantiate this class, specify the filename extensions you want and a possible starting location, and then call the PickSingleFileAsync method.
Notice that method name: PickSingleFileAsync with the Async at the end. The PickSingleFileAsync method does not directly cause the file-open picker to be displayed because the file-open picker needs to interrogate the file system, and that might take longer than 50 milliseconds.
Instead, PickSingleFileAsync returns an object of type PickSingleFileOperation. (All the asynchronous methods return an object that ends with the word Operation.) This class implements the IAsyncOperation<StorageFile> interface. That StorageFile type is important, because that's what the PickSingleFileAsync will eventually return to your program, but just not right away.
IAsyncOperation<T> derives from the IAsyncInfo interface, which defines methods named Start, Cancel, and Close, and properties named Id, Status, and ErrorCode. The IAsyncOperation<T> interface additionally defines a property named Completed. The type of this Completed property is a delegate of a callback method that you must supply. After your program sets this Completed method, it can call Start. Your program then finishes anything else it may be doing and returns control back to Windows.
When Windows is ready to deliver a file back to your program, the callback method is called, and the callback method calls GetResults, which returns an object of type StorageFile. (Don't worry: I'm going to show you code in a moment.)
Once your program has a StorageFile object referencing a file that the user picked, you can open that file for reading with a method named OpenAsync. Yikes, another Async method!
Let's look at some code. Here's how you might write this pair of Async calls using traditional callback methods:
void OnButtonClick(object sender, RoutedEventArgs args)
{
FileOpenPicker fileOpenPicker = new FileOpenPicker
{
CommitButtonText = "Open",
FileTypeFilter = { ".png", ".jpg", ".jpeg", ".bmp" },
SuggestedStartLocation = PickerLocationId.PicturesLibrary,
ViewMode = PickerViewMode.List
};
PickSingleFileOperation pickFileOp = fileOpenPicker.PickSingleFileAsync();
pickFileOp.Completed = OnPickSingleFileAsyncCompleted;
pickFileOp.Start();
}
void OnPickSingleFileAsyncCompleted(IAsyncOperation<StorageFile> pickFileOp)
{
StorageFile storageFile = pickFileOp.GetResults();
if (storageFile == null)
return;
StreamRetrievalOperation streamOp =
storageFile.OpenAsync(FileAccessMode.Read);
streamOp.Completed = OnOpenAsyncCompleted;
streamOp.Start();
}
void OnOpenAsyncCompleted(IAsyncOperation<IRandomAccessStream> streamOp)
{
IRandomAccessStream stream = streamOp.GetResults();
BitmapImage bitmap = new BitmapImage();
bitmap.SetSource(stream);
image.Source = bitmap;
}
The handler for the button click instantiates and initializes the FileOpenPicker and calls PickSingleFileAsync, which returns an object of type PickSingleFileOperation. The OnPickSingleFileAsyncCompleted method in the application is set to the Completed property of this PickSingleFileOperation object. (Notice that Completed is a property rather than an event, but it works much the same as an event.) The OnButtonClick method concludes by calling Start on this Operation object. If the OnButtonClick method had anything else to do, that code would be executed before the asynchronous method would start doing its work.
Sometime after OnButtonClick returns control to Windows, the file-open picker screen is displayed. During this time, other code in the program could run. When the user dismisses that picker (either by selecting a file or pressing the Cancel button), the OnPickSingleFileAsyncCompleted handler in the application is called. Although the argument to the callback is defined to be of type ISyncOperation<StorageFile>, this is really the same type as the PickSingleFileOperation object originally returned from PickSingleFileAsync. (In general, it might or might not also be the same object.) Call GetResults on that to get the StorageFile object. If it's null, that means the user dismissed the picker using the Cancel button.
Otherwise, the program needs to continue with its second asynchronous method call. This is the OpenAsync method of the new StorageFile. Get back a StreamRetrievalOperation. Set the Completed handler and call Start. When the operation concludes, the OnOpenAsyncCompleted callback method is called, and the GetResults method returns an object of type IRandomAccessStream.
I've not included with this blog entry an actual program that contains the code I've just shown you because I couldn't actually get this code to work. This is partially due to the Completed methods running on non-UI threads, but even using Dispatcher and BeginInvoke, I couldn't get the BitmapImage code to work.
At any rate, one of the problems with the traditional technique is that you've spread the job out over three daisy-chained methods. If you wanted to share data between these methods, you'd be forced to use fields. This is one reason why C# programmers have become fond — and perhaps at times, overly fond — of anonymous lambda methods.
Here's the whole job moved to the OnButtonClick method where the two callback methods are anonymous using the lambda syntax:
void OnButtonClick(object sender, RoutedEventArgs args)
{
FileOpenPicker fileOpenPicker = new FileOpenPicker
{
CommitButtonText = "Open",
FileTypeFilter = { ".png", ".jpg", ".jpeg", ".bmp" },
SuggestedStartLocation = PickerLocationId.PicturesLibrary,
ViewMode = PickerViewMode.List
};
PickSingleFileOperation pickFileOp = fileOpenPicker.PickSingleFileAsync();
pickFileOp.Completed = (pickFileOp2) =>
{
StorageFile storageFile = pickFileOp2.GetResults();
if (storageFile == null)
return;
StreamRetrievalOperation streamOp =
storageFile.OpenAsync(FileAccessMode.Read);
streamOp.Completed = (streamOp2) =>
{
IRandomAccessStream stream = streamOp2.GetResults();
BitmapImage bitmap = new BitmapImage();
bitmap.SetSource(stream);
image.Source = bitmap;
};
streamOp.Start();
};
pickFileOp.Start();
}
Now everything happens in one method (kind of) but the structure of the code has suffered as a result. The code has been turned inside-out: The call to the Start method of the PickSingleFileOperation object seems to be after the call to the Start method of StreamRetrievalOperation but they're actually called in the opposite order, and then after each Start call, execution continues with the Completed handlers that occur earlier in the method. Imagine if you need to daisy-chain four or five or six asynchronous operations! (And that's not so inconceivable.)
To the rescue comes the new C# 5.0 keyword await. Here's the equivalent code using the await operator:
async void OnButtonClick(object sender, RoutedEventArgs args)
{
FileOpenPicker fileOpenPicker = new FileOpenPicker
{
CommitButtonText = "Open",
FileTypeFilter = { ".png", ".jpg", ".jpeg", ".bmp" },
SuggestedStartLocation = PickerLocationId.PicturesLibrary,
ViewMode = PickerViewMode.List
};
StorageFile storageFile = await fileOpenPicker.PickSingleFileAsync();
if (storageFile == null)
return;
IRandomAccessStream stream =
await storageFile.OpenAsync(FileAccessMode.Read);
BitmapImage bitmap = new BitmapImage();
bitmap.SetSource(stream);
image.Source = bitmap;
}
Notice the two occurrences of await that's I've highlighted in red. They precede the two asynchronous methods. Although PickSingleFileAsync actually returns a PickSingleFileOperation object on which you must normally set a Completed handler and then call Start and then call GetResults when the Completed callback is called to get a StorageFile object, the await operator seems to bypass all the messy stuff and simply return the StorageFile directly. And that's exactly what it does, except not quite right away.
It looks like magic, but this is why we use high-level languages, right? We want to hide away messy implementation details in simpler language constructs. Let the C# compiler generate the callback, the Start call, and the GetResults call. But what the await operator also does — and you'll probably want to check out Mads Togersen's article if this intrigues you — is turn the method into a state machine. The method begins executing normally, until PickSingleFileAsync is called and the first await appears.
That await does not wait until the operation completes. Instead, the OnButtonClick method is exited at that point. Control returns back to Windows. Other code on the program's user-interface thread can then run, as can the file-picker itself. When the file-picker is dismissed and a result is ready and the UI thread is ready to run some code, execution of the OnButtonClick method continues with the assignment to the storageFile variable, and then continues until the next await operator. And so forth with as many await operators as you like until the method completes.
We who like C# like it in part because we are most comfortable with imperative programming languages, and C# is a damn good one. Once we start using asynchronous operations with callbacks, however, the imperative structure of C# starts to slip away. The await operator brings back that imperative structure and turns asynchronous calls into what appears to be a series of sequential normal method calls. (But keep in mind that a method in which await appears is actually chopped up into pieces behind the scenes, much like the second example shown earlier with the lambda methods.)
Moreover, everything in that OnButtonClick example with await runs in the UI thread. This method actually works, and if you have a Windows 8 development environment installed, you can download the AsyncDemo project to check it out.
There are some restrictions on the await operator. It cannot appear in the catch or finally clause of an exception handler. However, it can appear in the try clause, and this is how you'll trap errors that occur in the asynchronous method. There are also ways to cancel operations, and some asynchronous methods report progress as well.
The method in which the await operator appears must be flagged as async, but the async keyword doesn't really do anything: In earlier versions of C#, await was not a keyword, so programmers could use the word for variable names or property names or whatever. Adding a new await keyword to C# 5.0 would break this code, but restricting await to methods flagged with async avoids that problem. The async modifier does not change the signature of the method — the method above is still a valid Click handler. But you can't use async (and hence await) on entry points, such as Main or class constructors.
You can also use these techniques with your own code. Suppose you have a method in your program that looks something like this:
double BigFreakingJob(int arg1, int args)
{
double val = 0;
// ... lengthy code
return val;
}
You probably don't want to call this method from your UI thread because... well, the method name seems to indicate that it would be unwise. You could put this method in a secondary thread, but once again, that process has been made easy for you.
I haven't been able to get the Windows 8 AsyncInfoFactory.Create to work, but I have gotten the similar .NET Task.Run to work. You can turn BigFreakingJob into an asynchronous method with barely more alterations than adding an Async suffix to the method name:
Task<double> BigFreakingJobAsync(int arg1, int arg2)
{
return Task.Run<double>(() =>
{
double val = 55;
// ... lengthy code
return val;
});
}
You're basically passing an anonymous method to Task.Run. The Task<double> return type is the .NET equivalent of the IAsyncOperation interface in Windows 8.
Now you can call that method using the await operator:
double x = await BigFreakingJobAsync(33, 55);
If, perchance, the body of the BigFreakingJobAsync method includes its own await operators, you would need to flag the anonymous method as async:
Task<double> BigFreakingJobAsync(int arg1, int arg2)
{
return Task.Run<double>(async () =>
{
double val = 55;
// ... lengthy code with await
return val;
});
}
And that's how you call asynchronous methods and write asynchronous methods without messing around with threads, or synchronization semaphores, or Dispatcher objects. The heavy crunching goes on in the background while the user interface remains — stand back, I'm going to say it — fast and fluid.