*N Async, Part 1
You’ve all read about the asynchrony promise of C# 5 (if you haven’t, I highly recommend reading Eric Lippert’s series about the subject or this post won’t make much sense). I think it’s a great step forward, and it would make asynchronous programming all a lot easier.
We already know how to think in Tasks instead of threads, .NET 4.0 taught us that.
We already know how to use continuations (or at least some weak form of it), C# 2.0 iterators taught us that.
So, as an experiment*, I went ahead and implemented a weak form of the await/async magic** using “the materials in the room”, i.e. tasks and iterators – minus the syntactic sugar, of course. Let’s have a look.
First, A Sample
In C# 5 we would have (taken from Anders’ Netflix sample):
- async void LoadMoviesAsync(int year)
- {
- while (true)
- {
- var movies = await QueryMoviesAsync(year, imageCount, pageSize, cts.Token);
- if (movies.Length == 0) break;
- DisplayMovies(movies);
- }
- }
While in my implementation it would look like this:
- IEnumerable<Task> LoadMovies(int year)
- {
- while (true)
- {
- var moviesTask = Async.Await<Movie[]>(result =>
- QueryMovies(result, year, imageCount, pageSize, cts.Token));
- yield return moviesTask;
- var movies = moviesTask.Result;
- if (movies.Length == 0) break;
- DisplayMovies(movies);
- }
- }
What’s going on here?
First of all, as you can see, the methods look very similar. We have created an iterator that allows us to start running the method and break after every yield. The implementation of Async.Await() method is surprisingly simple:
- The Await() method simply creates an enumerator, which starts up the state machine.
- It calls MoveNext(), which executes the method up to the next yield.
- We get a Task from the yield, and attach a continuation to it, which calls MoveNext() again, and so on.
- public static Task<T> Await<T>(Func<TaskCompletionSource<T>, IEnumerable<Task>> func)
- {
- var completionSource = new TaskCompletionSource<T>();
- Await(func(completionSource));
- return completionSource.Task;
- }
- public static void Await(IEnumerable<Task> iterator)
- {
- IEnumerator<Task> enumerator = iterator.GetEnumerator();
- Run(enumerator);
- }
- private static void Run(IEnumerator<Task> enumerator)
- {
- if (enumerator.MoveNext())
- {
- enumerator.Current.ContinueWith(t => Run(enumerator));
- }
- }
Why do we need TaskCompletionSource?
The return value of the iterator method must always be IEnumerable<Task>. But what if we want to return a value from a method? Remember, it doesn’t execute synchronously anymore! That is why methods that (originally) do not return void, have the option of adding an argument of TaskCompletionSource. In the method, we can call TaskCompletionSource.SetResult() to set the result. We also return the Task the TCS creates, so the caller could access the result. The overload of Await() that we use in this case simply wraps around this functionality, and enables a more concise syntax.
When an asynchronous method has no return value, TaskCompletionSource is not needed.
What’s missing?
In the next installment(s) we will discuss:
- Task Schedulers (how to make sure we’re in the right context for UI operations)
- Limited exception handling
* Yes, it means I wouldn’t recommend using this in “real” code – just wait for C# 5. This is just for fun.
** And I use the term very figuratively.
Tags
.NET 4 Animation AppFabric Async Axum Blog C# ClearType Cloud CLR CodeValue Contests Deep Zoom Experiments Generics Google Ink Lectures Modeling Performance Personal Pivot Prism Programming Languages Prolog Reflector RTL Sela Silverlight The Arbel Network Themes Threading Tips Visual Studio WCF Windows 7 Windows 2003 Windows Azure Windows Forms Windows Phone Windows Vista Windows XP WPF XAML ZuneArchives
- May 2013
- February 2013
- June 2012
- May 2012
- June 2011
- November 2010
- August 2010
- July 2010
- June 2010
- March 2010
- December 2009
- November 2009
- February 2009
- January 2009
- December 2008
- November 2008
- October 2008
- September 2008
- August 2008
- November 2007
- September 2007
- June 2007
- May 2007
- February 2007
- November 2006
- October 2006
- February 2006
- August 2005
- February 2005
- August 2004
- July 2004
- June 2004
- May 2004





