次の方法で共有


Tutorial: Pic Viewer Revisited on the Async CTP

As Soma announced this morning, we’ve just released the Visual Studio Async CTP, a preview of the new language support coming in VB and C# to make it far easier to write responsive client apps and scalable server apps.  You can find out more details by visiting the Async CTP site.

For this post, I’m going to continue my Pic Viewer WPF demo from last year, updating it to show what you can do with the Async CTP.  If you don’t yet have the Async CTP, you can get it up on the Download Center.  Just install it on top of Visual Studio 2010 RTM.  The installer will patch your existing compiler and IDE bits and save the old files in case you uninstall.

You’ll also need the starting project.  We’ll start mostly from where we left off on the Pic Viewer app last time, with an extra dropdown in place to choose between Internet picture feeds:


 
The dropdown lets you choose between various BBC News feeds that contain images.  The app as written is nifty, but if the network is slow, you’ll notice that it becomes unresponsive each time you request a new feed.  This is because WebClient.DownloadString performs the network request synchronously on the UI thread, blocking UI rendering until the request completes:


 
To keep the UI responsive while waiting for the request, we’ll want to switch to an asynchronous version of DownloadString.  The new language support in the Async CTP lets us easily make this transition, keeping our control flow straightforward.

Note that the code samples in this tutorial are written in C#, but the Async CTP also exposes the same feature with identical keywords in Visual Basic.

To start, let’s change our call to wc.DownloadString to instead call wc.DownloadStringTaskAsync, a new asynchronous version of DownloadString that follows the new Task-based asynchronous pattern:

By hovering over the compile error we’ve now introduced, I can see the type mismatch:

 

While DownloadString returned string, DownloadStringTaskAsync returns Task<string>.  Task<T> is a type introduced in .NET 4.0 as part of the Task Parallel Library, and represents an ongoing operation, specifically an operation that will have a value of type T when it completes.  By calling an API that returns Task<string>, we’re not receiving back a string, but rather a handle to get a string later, once the network request is completed.

To wait for the network operation to finish while keeping the UI responsive, let’s add the new “await” keyword before our DownloadStringTaskAsync call.  Adding “await” gives us a compile error that “await” may only occur in a method with the new “async” modifier, so let’s add that as well:


 
The “await” expression here tells the compiler to suspend executing the current method and return to the caller. Only when the awaited Task (in this case a Task<string>) completes, producing the string to assign to “text”, is execution of the method resumed. In the meantime, no thread is occupied by this method.  The way “await” works behind the scenes is that the compiler schedules “the rest of the LoadFromRss method” as the callback to invoke upon completion.

This is similar to what we’d do manually when calling a traditional async API (such as DownloadStringAsync), but in this case we get to maintain the natural control flow of the try block and the sequence of statements inside.  The only difference between our synchronous and asynchronous implementation is two keywords and a call to a different method.  Hit F5 and observe that our program now stays responsive while it awaits a reply from the server:


 
Though our app is now responsive, if you’re observant you’ll notice that each time you change categories you first see one image from the previous category before Prev and Next bring you to the new category.  To see why this is, let’s find the method that calls our LoadFromRss method.

To do this, we’ll use Call Hierarchy, a new feature available in Visual Studio 2010 Professional and above.  Right-click “LoadFromRss” and choose “View Call Hierarchy” from the context menu.


 
This brings up the Call Hierarchy pane, letting us easily find all methods that relate to LoadFromRss.  From here we expand “Calls To ‘LoadFromRss’”:


 
Now that we’ve found the caller, Feeds_SelectionChanged, we can double-click it in the list to jump there:


 
We can now see the problem.  LoadFromRss kicks off an asynchronous network request, but since it returns void, we have no way to know when that request completes.  Once LoadFromRss “awaits” its network request, it immediately suspends its execution, letting Feeds_SelectionChanged continue.  This means that buttonNext_Click gets called before LoadFromRss finishes switching feeds.

What we’d like to do here is “await” LoadFromRss, so that Feeds_SelectionChanged will not continue executing until LoadFromRss is finished with its network request.  We start by adding the “await”, along with the “async” modifier the compiler asks us to add:


 
The compiler then reminds us that we cannot await a method that returns void. We must await a Task or Task<T>, since the compiler must have an object that can signal when the async operation completes.

Therefore, let’s make LoadFromRss return Task instead, a type that signals completion but does not return a value:


 
It turns out the return type is all we have to change.  The compiler knows this is an async method and does the magic necessary to produce a Task that will signal completion when control reaches the end of the method.

If we run the program, we now see that choosing a feed shows an image from that feed right away:


 
Now that our application loads its data asynchronously and remains responsive, we can easily provide related functionality, such as a progress bar or a Cancel button, which I’ll leave as an exercise to the reader.  For more ideas, check out the samples included in the Async CTP under “Visual Studio Async CTP/Async Readme” on the Start Menu.

Enjoy!

Comments

  • Anonymous
    October 28, 2010
    Hi there... you've mentioned the fact that you can uninstall the CTP - how does one go about it?  It's not obvious at all to me.

  • Anonymous
    October 28, 2010
    The comment has been removed

  • Anonymous
    October 28, 2010
    The comment has been removed

  • Anonymous
    October 28, 2010
    That's really cool. Now we don't need that old multi threading implementation for windows apps.

  • Anonymous
    November 03, 2010
    Please make this part of the WP7 SDK :)

  • Anonymous
    November 10, 2010
    Hi All, I wanted to make sure everyone is aware of the Async CTP Forum we've set up on MSDN: social.msdn.microsoft.com/.../threads Please join us there to discuss further questions that you may have on the Async CTP! Thanks, Lisa Feigenbaum Lisa (dot) Feigenbaum (at) microsoft (dot) com Visual Studio Community Program Manager