Asynchrony in C# 5, Part One

Asynchrony in C# 5, Part One

Rate This
  • Comments 61

The designers of C# 2.0 realized that writing iterator logic was painful. So they added iterator blocks. That way the compiler could figure out how to build a state machine that could store the continuation - the “what comes next” - in state somewhere, hidden behind the scenes, so that you don’t have to write that code.

They also realized that writing little methods that make use of local variables was painful. So they added anonymous methods. That way the compiler could figure out how to hoist the locals to a closure class, so that you don't have to write that code.

The designers of C# 3.0 realized that writing code that sorts, filters, joins, groups and summarizes complex data sets was painful. So they added query comprehensions and all the rest of the LINQ features. That way the compiler could figure out how to do the right object model calls to build the query, the expression trees, and so on.

The designers of C# 4.0 realized that interoperating with modern and legacy dynamic object models was painful. So they added the dynamic type. That way the compiler could figure out how to generate the code at compile time that does the analysis in the Dynamic Language Runtime at runtime.

The designers of C# 5.0 realized that writing asynchronous code is painful, in so many ways. Asynchronous code is hard to reason about, and as we've seen, the transformation into a continuation is complex and leads to code replete with mechanisms that obscure the meaning of the code.

This shall not stand.

I am pleased to announce that there will be a C# 5.0 (*), and that in C# 5.0 you’ll be able to take this synchronous code:

void ArchiveDocuments(List<Url> urls)
{
  for(int i = 0; i < urls.Count; ++i)
    Archive(Fetch(urls[i]));
}

and, given reasonable implementations of the FetchAsync and ArchiveAsync methods, transform it into this code to achieve the goal of sharing wait times as described yesterday:

async void ArchiveDocuments(List<Url> urls)
{
  Task archive = null;
  for(int i = 0; i < urls.Count; ++i)
  {
    var document = await FetchAsync(urls[i]);
    if (archive != null)
      await archive;
    archive = ArchiveAsync(document);
  }
}

Where is the state machine code, the lambdas, the continuations, the checks to see if the task is already complete? They’re all still there. Let the compiler generate all that stuff for you, just like you let the compiler generate code for iterator blocks, closures, expression trees, query comprehensions and dynamic calls. The C# 5.0 code above is essentially a syntactic sugar for the code I presented yesterday. That's a pretty sweet sugar!

I want to be quite clear on this point: the action of the code above will be logically the same as the action of yesterday's code. Whenever a task is "awaited", the remainder of the current method is signed up as a continuation of the task, and then control immediately returns to the caller. When the task completes, the continuation is invoked and the method starts up where it was before.

If I’ve timed the posting of this article correctly then Anders is announcing this new language feature at the PDC right about… now. You can watch it here.

We are as of right now making a Community Technology Preview edition of the prototype C# 5.0 compiler available. The prototype compiler will be of prototype quality; expect features to be rough around the edges still. The idea is to give you a chance to try it and see what you think so that we can get early feedback.

I'll be posting more about this feature tomorrow and for quite some time after that; if you can't wait, or want to get your hands on the prototype, you can get lots more information and fresh tasty compiler bits from msdn.com/vstudio/async.

And of course, in keeping with our co-evolution strategy, there will be a Visual Basic 11 and it will also feature task-based asynchrony. Check out the VB team blog for details, or read all about this feature at my colleague Lucian's blog. (Lucian did much of the design and prototyping of this feature for both C# and VB; he, not I, is the expert on this so if you have deep questions, you might want to ask him, not me.)

Tomorrow: await? async? Task? what about AsyncThingy<T>? Tell me more!

(*) We are absolutely positively not announcing any dates or ship vehicles at this time, so don't even ask. Even if I knew, which I don't, and even if my knowledge had the faintest chance of being accurate, which it doesn't, I still wouldn't tell you.

  • Wow. Beautiful.

    This was my idea :) I wrote MessagingBus (@codeplex) library for that which is more flexible yet more ugly. Currently I'm writing 2.0 version with dynamically created proxies to make the code more beautiful.

    Problems with async & await: 1) How many threads will do the job? 2) Any priority of handling async methods? 3) How to delegate execution to another machine with .Net remoting? - I estimate, there will be attributes for specifying that.

    Neat feature.

  • @Bohdan: async and await themselves deal with tasks, not threads. How a particular task executes is up to that task's implementation. Some tasks spin up threads (and, again, it's up to them where the threads come from, and how many). I/O stuff would probably use Win32 APIs with callbacks, so no threads involved at all.

  • @Bohdan Trotsenko

    "How many threads will do the job"

    Needn't be multiple threads involved at all. A GUI-based app, single threaded, could have a message-loop timer event, and expose it as a Task (or syntactically Task-like object) that finishes when the time period ends. In an async method you'd say:

    // do stuff

    await Timer.AsTask(5);

    // do more stuff

    AsTask would return the Task, the compiler would turn the async method into a restartable state machine, and it would be equivalent to enlisting on the boring old GUI timer event a handler lambda that would do-more-stuff.

  • Can't wait...

  • Nice!  All these years of inability to easily update GUI... Gone!!!!!

  • blogs.msdn.com/.../asynchrony-in-c-5-part-one.aspx

    blogs.msdn.com/.../asynchrony-in-c-5-part-one.aspx

    blogs.msdn.com/.../asynchrony-in-c-5-part-one.aspx

  • I like it! I don't care what other people are saying about redundancy.. blah blah.. I think it's cool!

  • This is a great feature and makes me jealous that I don't write in C# all the time.

    One question - what happens when debugging? When stopped, what would the call stack show - the code as written or the code as generated?

  • Yes! This makes me so happy. In 2007 I posted this question - www.msdotnet.org/interrupting-flow-of-a-function-and-or-yielding-control-t355895.html - and a few people from the CLR team have either participated or seen this. The kind of things that we were trying to do are now really possible without having to host CLR :)

  • I don't want asynchrousity. i wont more generous programming!

    Whati  talking about? Consider this please:

    class A<B> where B has { [static ]void SuperFunc(); } {////}

    A wil accept any type that has public void SuperFunc(); method.

    if static specified, look for public static coid SuperMethod() method, for instance method otherwise.

    class A<B> where B like [static] ISuperInterface {///}

    A will accept any type that has at least all methods and properties of ISuperInterface  as public members.

    if static is specified, require fur these members as static, and so on. If B implemnts ISuperInterface in a usual way, it is acceplable too.

    Lets declare some operators:

    public static abstract class MathBase<T>

    {

       public abstract static T operator+(T a, T b) {,,,,};

       public abstract static T operator-(T a, T b) {,,,,};

       public abstract static T operator*(T a, T b) {,,,,};

       public abstract static T operator/(T a, T b) {,,,,};

    }

    Lets make it more specific:

    public virtual static class SuperMath : MathBase<SuperNumber>

    {

       public override static SuperNumber operator+(SuperNumber a, SuperNumber b) {,,,,};

       public override static SuperNumber operator-(SuperNumber a, SuperNumber b) {,,,,};

       public override static SuperNumber operator*(SuperNumber a, SuperNumber b) {,,,,};

       public override static SuperNumber operator/(SuperNumber a, SuperNumber b) {,,,,};

    }

    And so on....

    Or let we allow this:

    public staic delegate T Sum<T>(this IEnumerable<T> items) where T has { static T operator+(T a, T b) } {...}

    and now we can sum anithing that has the addition operator.

    I sure it will open the new era of generic rogramming,,, ir at least can..

  • strange how i was just this morning thinking about how this is still the one painful part of .net/c#... this will be such a hugely needed improvement.

  • what if some Fetch call never returns?

  • Is alredy out some version (beta, alpha)  of the .Net Framework 5 ?

  • Is this solution really optimal from the asynchrony point of view? After await FetchAsync(urls[i]); we basically call await archive; (which can yield execution) without starting fetch of the next document, right? And we don't await on the last archive (not sure if this is needed).

    My proposal would be to change the code as follows:

    async void ArchiveDocuments(List<Url> urls)

    {

       Task archiveTask = null;

       Task<Document> documentTask = FetchAsync(urls[0]);

       for(int i = 0; i < urls.Count; ++i)

       {

           var document = await documentTask;

           if (i+1 < urls.Count)

               documentTask = FetchAsync(urls[i+1])

           if (archiveTask != null)

               await archiveTask;

           archiveTask = ArchiveAsync(document);

       }

       await archiveTask;

    }

    Maybe this is really just nitpicking, but the code from this article seems to be the reference example code on async/await feature for now.

  • (well, I forgot checking for the empty list, my bad)

Page 4 of 5 (61 items) 12345