Sdílet prostřednictvím


Refreshing the Async CTP

Good morning everyone! I am pleased to tell you that the C# and VB teams are announcing a "refresh" of the async Community Technology Preview at MIX11 today, and that it is as of right now available on the Async CTP site.

Recall that the CTP release is an early look at our thinking for the proposed async language features so that we can get your feedback. Rather than posting feedback here, please let us know what you think on the Async Forum.

We've gotten a lot of good feedback so far; this refresh is intended to address the top ten-or-so concerns voiced so far about the CTP, namely:

  • The async feature can now be used for Windows Phone 7 development. Woo hoo!
  • The new CTP works with Visual Studio 2010 Service Pack 1 and with non-English-localized versions. (Note that the CTP itself has not been localized into any language other than English, but it should now work with non-English versions of VS2010.)
  • The samples show some of our thinking on how unit testing might work with the async feature.
  • The license is now an "as is" EULA; that is to say that you are permitted to use this technology in real live deployed apps if you choose to do so at your own risk. Note that (1) I am not a lawyer, so don't ask me for legal advice about the EULA and (2) I am not recommending that you go live with this! This is still a very early build of this technology. But if you and your customers like living on that leading edge, you can do so.
  • There are a number of new features in System.Threading.Tasks.Dataflow.dll.
  • AsyncCtpLibrary_Phone.dll and AsyncCtpLibrary_Silverlight.dll now have full support for tasks.
  • Unhandled exceptions in async void methods are now posted to the current synchronization context.
  • We've made some changes to the "awaiter" pattern that enable better performance.
  • The state machine transformation now aggressively nulls out fields no longer in use, enabling more efficient garbage collection.
  • A number of bugs involving race conditions with finally blocks, other code generation errors, library code and the IDE have been fixed.

Watch Lucian's blog and the Async Forum for more details on precisely what the changes outlined above are. Also, Mads and Alex have just posted interviews on Channel 9 giving an overview of the various changes in video form. Their videos are here and here.

Some tips I've heard on getting it installed from those in the know:

  • Uninstall previous versions of the CTP.
  • If you're doing phone development, install the phone developer tools before you install SP1.
  • Install SP1 before installing the CTP refresh.
  • If you install a "hotfix" after SP1, it might not be compatible with the CTP.

I encourage you to check it out and post any thoughts or questions you might have to the forum. Thanks!

And as always, thanks to my colleagues Mads, Alex and Lucian for their leadership in this feature area, and for providing this list of changes.

Comments

  • Anonymous
    April 13, 2011
    Yay! Thanks for this - I can finally install SP1 :) I've just downloaded the spec, and I can't immediately see any changes to the awaiter pattern compared with the previous version... can you enlighten us as to what these are? Or were they just implementation changes instead of spec changes? I'll be interested to see whether my solution of "async without the AsyncCtpLibrary.dll" projects still builds... (Will try awaiting a dynamic expression as well, as I believe that will work now.)

  • Anonymous
    April 13, 2011
    Have worked out why I couldn't see any difference in the spec: the one I downloaded from the async CTP site today is actually the same as the old one. (At least, they have the same MD5 sum.) Any idea if this is actually due to caching (which I've certainly seen as an issue on MSDN sites before) or just a delay in making the new spec available?

  • Anonymous
    April 13, 2011
    Final comment for the moment, I promise: looks like it's a client-side caching issue. Downloading in a different browser fixed it. Hope this helps anyone else downloading bits from the site.

  • Anonymous
    April 13, 2011
    This is not directly related question, but are you planning to fix how the foreach statement  is handling clojures ? You had a blog about it a few years back at blogs.msdn.com/.../closing-over-the-loop-variable-considered-harmful.aspx but I could not find any statement about it since C# 4 came up.

  • Anonymous
    April 13, 2011
    The foreach closure issue is certainly one I've burnt my hand on now. Interesting post. Showing a warnings seem to be the way to go. Even if the compiler can't detect a problem in the general case, a warning would certainly be beneficial where the compiler can clearly see that the delegate is getting reused outside of the loop in the scope of the local function.

  • Anonymous
    April 13, 2011
    It seems like a shame that await expressions can't be used in most places in query expressions because query expressions can't generate async lambdas. Is there any reason that await expressions can't just be detected and cause their enclosing lambdas to be made into async lambdas? I'd expect that async query expressions would allow something similar to F#'s async workflows, or maybe allow nice composition with Rx. Am I wrong? Is there no real good use case for async query expressions?

  • Anonymous
    April 14, 2011
    dmihailescu: Personally I would like to see for loops changed to the iterator being inside the block (ie same as Lua), as it'd save me from having a heap of redundant variables with silly names (ie normally "j" and "localJ", or for foreach's "item" and "localItem") - which just make the code harder to understand, and I'm always afraid someone will remove the "localJ" without understanding it's purpose. Often it's in a lambda that is consumed right then and there, but I still have to copy it to a new variable just to keep ReSharper happy.. But alas, as there's probably 1 or 2 programs in the known universe that depend on the current feature (I can't imagine how or why), it'll never be changed. My question (sorry if you've already read it at the end of the last comments thread) - any chance we can have IReadList added, an IWriteList added, an IExpandableWriteList (ie Clear, Add - array would ideally not implement this) added, and IList inherit from IExpandableWriteList and IReadList? I have a raft of classes that all only half implement the IList interface, throwing exceptions on the other methods.. which tends to indicate a poorly selected interface, but I'm offered no other choice. I know existing classes (ie Arrays) would have to continue to inherit from IList, but at least my own classes wouldn't have to and arrays and my own alike would all be compatible with methods expecting an IReadOnlyList (ie, a method that doesn't modify the list it's passed). Also I realise ICollection would have to be broken down similarly - but you know what? It should have been from the start =). (Sorry if double post)

  • Anonymous
    April 15, 2011
    @Alex: Problem with IReadOnlyList and a dervived IList is that if a bad guy have a reference to an IReadOnlyList, he can try to cast it to an IList. If he will success, you lose. You cannot avoid the readonly wrapper classes, if you want safe readonly collections.

  • Anonymous
    April 15, 2011
    Szindbad: The reason for having the IReadOnlyList interface isn't to prevent your list from being modified, it's for co/contra-variance. You want to be able to pass a List<Giraffe> to something expecting IReadOnlyList<Animal> or pass a List<Animal> to something expecting IExpandableWriteList<Giraffe>.

  • Anonymous
    April 15, 2011
    @Szindbad Trying to protect against bad guys like that whilst there's still reflection is mostly futile. I want it for co/contra-variance like Gabe says - but also because you often only have an IReadOnlyList. ie, look at the Enumerable.Repeat / Enumerable.Range - they currently return enumerables, losing the count information and making ToArray/ToList slower than necessary operators, making the idiom Enumerable.Repeat(0x55, numberOf55s).ToArray() unwise (20 times slower than a for loop). If they'd just implemented IReadOnlyList they could have attached the count to the returned results and this would become the recommended way of initialising an array to a value. Possibly a bad example though - my most recent annoyance brought on by lack of IReadOnlyList has been on a popsicle-immutable BitVector class (similar to BitArray, just with a builder and a .MakeVector() method - I do a lot of bit operations talking to a device via RS232, this is very nice to have). It's just really annoying though that the immutable BitVector has to implement IList to really be usable - but to do that it has to throw on .Add(), .Clear(), .Remove(), .Insert(), .RemoveAt(), and this[].Set. Even the builder - which is a fixed size once created (same as BitArray) needs to throw on .Add(), .Clear() (I could zero it, but lets be consistent with what IList.Clear() usually means), .Remove(), .RemoveAt(), and .Insert(). So I've got two classes, both of which implement IList, but both of which have to throw on what seems to be the majority of methods - but yet there is no better interface for me to implement. And then worst of all - whenever I pass one of these collections expecting IList, I know I'm not passing them a true IList - and their method could in theory throw an exception at any moment. And then it's not only IList - imagine if there was an IReadOnlyCollection. All of Eric's immutability in C examples, the Deque, the Stack, the Queue, the AVLTree - add a count to each of those (yes it'll cost memory, but very useful to have) and they could all implement it, making ToArray/ToList extremely cheap, and allowing the Count to be retrieved via interface. But alas.

  • Anonymous
    April 18, 2011
    > Trying to protect against bad guys like that whilst there's still reflection is mostly futile. Not in a sandbox, where visibility rules are enforced even for reflection (so you can't invoke a private member of another class etc). That said, the problem here is that your interface name is a misnomer - IReadOnlyList implies that the list in question is read-only, but it's not true; the interface provided only allows read operations, but someone else may have a write interface to the same list and use it to update. A more precise name would be IReadableList.

  • Anonymous
    April 18, 2011
    IReadOnlyList is indeed just a read-only interface to a list, but that's exactly what it says on the tin. If the list were immutable then it should've been called ImmutableList.

  • Anonymous
    April 19, 2011
    The established practice in .NET is that "read-only" has the same meaning as "immutable". To wit, the "readonly" modifier in C#, and ReadOnlyCollection<T> class in BCL. The contract for both is that no-one can mutate the data, not that only the particular user of the reference cannot.

  • Anonymous
    April 19, 2011
    Actually, my mistake - ReadOnlyCollection<T> is in fact a wrapper around a potentially mutable collection. So in fact the established practice is exactly the opposite of what I wrote, and IReadOnlyList would make perfect sense. My apologies for the mistake.

  • Anonymous
    April 19, 2011
    So, just now I'm modifying an internal class that takes two IEnumerables and stores them both in public readonly properties (with readonly backing fields). Now I'm modifying what action it performs on these IEnumerables, and it now requires that it accesses them out-of-order (rather than sequentially). Now I know (internal class) that it's currently being created with List objects, so the logical solution is to change the constructor to accept two ILists, and modify the properties to be ILists too (this class is regularly recreated with the same lists, so this is required). Problem is - on creating the class, should I wrap the passed ILists in ReadOnlyCollections? Even though it's an internal class? Or do I just document that you must promise not to modify the properties? Just trying to demonstrate the daily cases that working with ILists raises issues. IEnumerable is clear. You pass a constructor an IEnumerable, and you know it isn't going to go ahead and publish it on a public property as a mutable sequence, because it cannot. Pass a constructor an IList - and without good documentation you have no idea what it's going to do to it. You either need to defensively wrap it in a ReadOnlyCollection, make an assumption on what the constructor does with it, or start digging through the internals of that class to see what it does. So you decide to wrap it in a ReadOnlyCollection - only to discover that the class also defensively wraps it in a ReadOnlyCollection to prevent people from modifying the list through it's public properties. Double defensive, and for no gain as everyone's well-behaved using this internal class anyway. It's not fun =(. IEnumerable is great.. but the very moment you need to index a passed collection it all falls in a heap. Anyway, last post I'll make on this - I should have put it on SO anyway (as a "What do you do if you need to publish an IList as a public property" topic).

  • Anonymous
    April 21, 2011
    Please confirm or deny that "Async CTP (SP1 Refresh)" is compatible and tested with ASP.NET MVC 2. If not, when it will be compatible? Thanks.

  • Anonymous
    May 04, 2011
    All these changes address improvements of the existing features. Did anyone raise the question whether async/await as language extension makes sense in the first place? (See ajdotnet.wordpress.com/.../visual-studio-async-ctp-an-analysis for my concerns.)