C# 5: At last, async without the pain

Posted by Alex.Davies on Simple Talk See other posts from Simple Talk or by Alex.Davies
Published on Sun, 04 Mar 2012 16:47:00 GMT Indexed on 2012/03/18 18:16 UTC
Read the original article Hit count: 253

Filed under:

For me, the best feature in Visual Studio 11 is the async and await keywords that come with C# 5.

I am a big fan of asynchronous programming: it frees up resources, in particular the thread that a piece of code needs to run in. That lets that thread run something else, while waiting for your long-running operation to complete. That's really important if that thread is the UI thread, or if it's holding a lock because it accesses some data structure.

Before C# 5, I think I was about the only person in the world who really cared about asynchronous programming. The trouble was that you had to go to extreme lengths to make code asynchronous. I would forever be writing methods that, instead of returning a value, accepted an extra argument that is a "continuation". Then, when calling the method, I'd have to pass a lambda in to it, which contained all the stuff that needed to happen after the method finished. Here is a real snippet of code that is in .NET Demon:

m_BuildControl.FilterEnabledForBuilding(    
    projects,
    enabledProjects => m_OutOfDateProjectFinder.FilterNeedsBuilding(
        enabledProjects,
        newDirtyProjects =>
        {
            // Mark any currently broken projects as dirty
            newDirtyProjects.UnionWith(m_BrokenProjects);

            // Copy what we found into the set of dirty things
            m_DirtyProjects = newDirtyProjects;

            RunSomeBuilds();
        }));

It's just obtuse. Who puts a lambda inside a lambda like that? Well, me obviously. But surely enabledProjects should just be the return value of FilterEnabledForBuilding? And newDirtyProjects should just be the return value of FilterNeedsBuilding?

C# 5 async/await lets you write asynchronous code without it looking so stupid. Here's what I plan to change that code to, once we upgrade to VS 11:

var enabledProjects = await m_BuildControl.FilterEnabledForBuilding(projects); 

var newDirtyProjects = await m_OutOfDateProjectFinder.FilterNeedsBuilding(enabledProjects);

// Mark any currently broken projects as dirty
newDirtyProjects.UnionWith(m_BrokenProjects);

// Copy what we found into the set of dirty things
m_DirtyProjects = newDirtyProjects;

RunSomeBuilds();

Much easier to read! But how is this the same code? If we were on the UI thread, doesn't the UI thread have to block while FilterEnabledForBuilding runs?

No, it doesn't, and that's the magic of the await keyword! It cuts your method up into its constituent pieces, much like I did manually with lambdas before. When you run it, only the piece up to the first await actually runs. The rest is passed to FilterEnabledForBuilding as a continuation, which will get called back whenever that method is finished. In the meantime, our thread returns, and can go back to making the UI responsive, or whatever else threads do in their spare time.

This is actually a massive simplification, and if you're interested in all the gory details, and speed hacks that the await keyword actually does for you, I recommend Jon Skeet's blog posts about it.

© Simple Talk or respective owner