Almost any software application today will likely contain a long-running process. “Long-running” may be a relative term but in the Windows Runtime it is specifically anything that could take longer than 50ms to execute. That’s a fairly small window, and it means those operations will need to run concurrently to the main application thread. Concurrency is important in both client applications (to keep from blocking the UI) and server applications (to accommodate multiple simultaneous requests).
The new technology referred to as Visual Studio Asynchronous Programming provides a streamlined language syntax for asynchronous development. It does this by providing two new keywords: async and await. While these keywords may simplify asynchronous development, they can still be confusing to developers. There are a lot of materials out there but I thought it might help to take a very simple example and explore just what these keywords are and how they operate. In this post I’ll focus specifically on the .NET Framework 4.5 support. While they are also supported for Metro-style applications, the implementation is slightly different.
The Main Event
In the movie Mission Impossible II, the short-lived protagonist Dr. Nekhorvich says:
“…every search for a hero must begin with something every hero needs, a villain. So in a search for our hero, Bellerophon, we have created a more effective monster: Chimera.”
In the search for an elegant solution to asynchronous programming we must start with some of the rougher implementations that have plagued developers in the past.
The event-based pattern is probably one of the most well-known asynchronous patterns to .NET developers as it is prevalent throughout the base library. Let’s assume I have a method that multiplies two numbers and for some crazy reason (maybe I’m sending it over a 300 baud modem to my Commodore 64 to process the result on the 6502 chip … you know, using a bunch of ROR operations) it takes a bit longer to process than I’d like, so I want to make sure it executes asynchronously. The first thing I’ll do is create an event argument payload for the result:
public class MultiplyEventArgs : EventArgs { public int Result { get; private set; } public MultiplyEventArgs(int result) { Result = result; } }
Next, I’ll define an interface:
public interface IMultiplierEvent { event EventHandler<MultiplyEventArgs> MultiplyCompleted; void MultiplyAsync(int a, int b); }
Finally, I’ll implement the class that executes the operation asynchronous and fires the completed event when done.
public class MultiplierEvent : IMultiplierEvent { public event EventHandler<MultiplyEventArgs> MultiplyCompleted; private void RaiseCompleted(int result) { var handler = MultiplyCompleted; if (handler != null) { handler(this, new MultiplyEventArgs(result)); } } public void MultiplyAsync(int a, int b) { Task.Run(() => RaiseCompleted(a * b)); } }
I can test this in a simple console program with a method call:
class Program { static void Main(string[] args) { var p = new Program(); p.DoEvent(); Console.ReadLine(); } }
The implementation requires two methods, one to set it up and another to receive the completed event. Not bad, but you can imagine handling a chain of multiple events could get ugly quickly.
public void DoEvent() { Console.WriteLine("Firing as an event 2 * 3 ..."); IMultiplierEvent eventBased = new MultiplierEvent(); eventBased.MultiplyCompleted += eventBased_MultiplyCompleted; eventBased.MultiplyAsync(2, 3); } void eventBased_MultiplyCompleted(object sender, MultiplyEventArgs e) { Console.WriteLine("Event result: {0}", e.Result); }
That’s the old world of events.
The Asynchronous Programming Model
Another popular asynchronous model is the Asynchronous Programming Model, or APM. The framework provides the IAsyncResult interface and you specify a pair of methods to Begin and End the operation. The first method always returns the IAsyncResult and the second method always takes the IAsyncResult and returns the result of the call.
In the implementation I create a nested class used to help maintain the state between the calls:
private class AsyncState { public Delegate MethodToCall { get; private set; } public object State { get; private set; } public AsyncState(Delegate methodToCall, object state) { MethodToCall = methodToCall; State = state; } }
Then I implement the methods. Notice that I’m casting the method call to a delegate and taking advantage of the built-in capability to invoke it asynchronously, then wrapping the end call to return the result.
public class MultiplierApm : IMultiplierApm { private class AsyncState [...] public IAsyncResult BeginMultiply(int a, int b, AsyncCallback callback, object state) { Func<int, int, int> multiply = Multiply; var asyncState = new AsyncState(multiply, state); return multiply.BeginInvoke(a, b, callback, asyncState); } public int EndMultiply(IAsyncResult asyncResult) { var asyncState = (AsyncState)asyncResult.AsyncState; var multiply = (Func<int, int, int>)asyncState.MethodToCall; return multiply.EndInvoke(asyncResult); } public int Multiply(int a, int b) { return a * b; } }
Now that I have an implementation, I can call it like this:
public void DoApm() { Console.WriteLine("Firing as APM 3 * 4 ..."); IMultiplierApm apmBased = new MultiplierApm(); apmBased.BeginMultiply(3, 4, result => { var value = apmBased.EndMultiply(result); Console.WriteLine("Apm result: {0}", value); }, null); }
Note that I might have used two methods for the APM as well, I simplify chose to take a short cut but using a lambda expression instead.
Taking Asynchronous Operations to Task
With the new task library, setting up and calling asynchronous operations is far easier than the previous two approaches. First, I can use a single method signature and simplify specify the need for asynchronous operations by wrapping the result in a Task:
public interface IMultiplier { Task<int> Multiply(int a, int b); }
For the implementation, I can easily use the built-in methods available in the library to spin off my thread:
public class Multiplier : IMultiplier { public Task<int> Multiply(int a, int b) { //return Task.Factory.StartNew(() => a * b); return Task.Run(() => a * b); } }
Finally, I can use the new keywords to make calling and waiting for the result easy. When I want to do something asynchronously without blocking the thread I’m on, I simply modify the method with the async keyword, then await the asynchronous operaiton. Again, it’s as simple as async to mark the method’s intention of spinning of a separate task to wait for, then await to launch the thread and receive the results in a single line of code.
public async void DoTask() { Console.WriteLine("Firing as task 4 * 5 ..."); IMultiplier taskBased = new Multiplier(); Console.WriteLine("Task result: {0}", await taskBased.Multiply(4, 5)); }
What happens here is that the current thread runs up until the await operation. There, the operation is spun off on a new thread. The main thread will wait for that thread to complete but will not block – it is not a synchronous operation. When the concurrent thread finishes what it is doing, it will drop the result back on the calling thread and allow me to interrogate the result. Notice that I can even nest the call inside of other operations – here the task must actually complete before it can pass the result to the string formatter which in turn sends the output to the console.
Simple interface design, simple implementation, and simple execution. I like it! But what do I do about those existing events and APM-based interfaces?
Wrapping Events
Fortunately, it’s possible to bundle up any asynchronous operation into a task. For this reason, I almost always declare my interfaces using Task. That way I can hide the underlying implementation. Let’s test this out. How can I take my IMultiplier interface and use it to call my MultiplierEvent class? The trick is to use a TaskCompletionSource. This special class allows me to perform an asynchronous operation using any pattern I prefer and then set a result when I’m done. The token will expose the event as a task that can be awaited. Here is a wrapper class that implements the simple interface:
public class MultiplierEventAsTask : IMultiplier { private IMultiplierEvent _event; public MultiplierEventAsTask() { _event = new MultiplierEvent(); } public Task<int> Multiply(int a, int b) { var tcs = new TaskCompletionSource<int>(); _event.MultiplyCompleted += (sender, args) => { tcs.SetResult(args.Result); }; _event.MultiplyAsync(a, b); return tcs.Task; } }
I can then call it the same way I did the task-based implementation.
Wrapping the APM
Wrapping the APM model is even easier because the library provides a set of functions to convert existing operations. The FromAsync method will take most APM-style calls and turn them into tasks:
public class MultiplierApmAsTask : IMultiplier { private IMultiplierApm _apm; public MultiplierApmAsTask() { _apm = new MultiplierApm(); } public Task<int> Multiply(int a, int b) { return Task<int>.Factory.FromAsync(_apm.BeginMultiply, _apm.EndMultiply, a, b, null); } }
Once again, the end result is the same simple interface despite a completely different implementation.
That’s a Wrap!
Using the Task we can now take various implementations and simplify them to a common, easy to use and understand interface. This little piece of code will execute each of the different implementations in order, waiting for the result but without blocking the main thread:
public async void DoAll() { // convert event to a task Console.WriteLine("Firing all converted events in order ..."); IMultiplier taskFromEvent = new MultiplierEventAsTask(); IMultiplier taskFromApm = new MultiplierApmAsTask(); IMultiplier task = new Multiplier(); Console.WriteLine("2 * 3 = {0}", await taskFromEvent.Multiply(2, 3)); Console.WriteLine("4 * 5 = {0}", await taskFromApm.Multiply(4, 5)); Console.WriteLine("6 * 7 = {0}", await task.Multiply(6, 7)); }
There is a lot more you can do than simply await tasks. You can combine tasks, chain tasks, even wait for one or all tasks to complete before moving onto another task. Once you understand that async simply specifies the intention to do something asynchronously without blocking the main thread, and await simply launches a task and returns the result once that task is complete, you should be well on your way to simplifying your asynchronous development.
Download the sample project here.