Diving deep with WinRT and await

AWS

Owner
FPCH Owner
Joined
Nov 19, 2003
Messages
11,230
Location
Florida U.S.A.
The recent blog post await[/b] keyword in C# and Visual Basic enables developers to use WinRT asynchronous operations while still maintaining and reasoning about good control flow. In this follow-on post, I dive much deeper into exactly how await works with WinRT. This knowledge will make it easier for you to reason about code that uses await, and as a result, will enable you to write better Metro style apps. To start, let’s ground ourselves by taking a look at a world without await. Reviewing the basics All of asynchrony in WinRT is rooted in a single interface: IAsyncInfo.

Every asynchronous operation in WinRT implements this interface, which provides the base functionality necessary to walk up to an asynchronous operation and inquire about its identity and status, and to request its cancellation. But this particular interface lacks what’s arguably the most important aspect of an asynchronous operation: a callback to notify a listener when the operation has completed. That capability is purposefully separated out into four other interfaces that all require IAsyncInfo, and every asynchronous operation in WinRT implements one of these four interfaces:

public interface <font color="#4bacc6">IAsyncAction</font> : <font color="#4bacc6">IAsyncInfo</font>
{
<font color="#4bacc6">AsyncActionCompletedHandler</font> Completed { <font color="#0000ff">get</font> <font color="#0000ff">set</font> }
void GetResults()
}

public interface <font color="#4bacc6">IAsyncOperation</font> : <font color="#4bacc6">IAsyncInfo</font>
{
<font color="#4bacc6">AsyncOperationCompletedHandler</font> Completed { <font color="#0000ff">get</font> <font color="#0000ff">set</font> }
TResult GetResults()
}

public interface <font color="#4bacc6">IAsyncActionWithProgress</font><TProgress> : <font color="#4bacc6">IAsyncInfo
</font>{
<font color="#4bacc6">AsyncActionWithProgressCompletedHandler</font><TProgress> Completed { <font color="#0000ff">get</font> <font color="#0000ff">set</font> }
<font color="#4bacc6">AsyncActionProgressHandler</font><TProgress> Progress { <font color="#0000ff">get</font> <font color="#0000ff">set</font> }
void GetResults()
}

public interface <font color="#4bacc6">IAsyncOperationWithProgress</font> : <font color="#4bacc6">IAsyncInfo</font>
{
<font color="#4bacc6">AsyncOperationWithProgressCompletedHandler</font> Completed { <font color="#0000ff">get</font> <font color="#0000ff">set</font> }
<font color="#4bacc6">AsyncOperationProgressHandler</font> Progress { <font color="#0000ff">get</font> <font color="#0000ff">set</font> }
TResult GetResults()
}</pre>

These four interfaces support all combinations of with-and-without results, and with-and-without progress reporting. All the interfaces expose a Completed property, which can be set to a delegate that is invoked when the operation completes. You may set the delegate only once, and if it’s set after the operation has already completed, it is immediately scheduled or invoked, with the implementation handling the race between the operation completing and the delegate being assigned.

Now, let’s say I wanted to implement a Metro style app with a XAML Button, such that clicking the button queues some work to the WinRT thread pool to perform a computationally-intensive operation. When that work completes, the content of the button is updated with the result of the operation.

How might we implement this? The WinRT ThreadPool class exposes a method to asynchronously run work on the pool:

public static <font color="#4bacc6">IAsyncAction</font> RunAsync(<font color="#4bacc6">WorkItemHandler</font> handler)</pre>

We can use this method to queue our computationally-intensive work so as to avoid blocking our UI thread during its run:

private void btnDoWork_Click(object sender, RoutedEventArgs e)
{
int result = 0
var op = ThreadPool.RunAsync(delegate { result = Compute() })
}</pre>

We now successfully offloaded the work from the UI thread to the pool, but how do we know when the work is done? RunAsync returns an IAsyncAction, so we can use a completion handler to receive that notification and run our continuation in response:

<font color="#a5a5a5">private void btnDoWork_Click(object sender, RoutedEventArgs e)
{
int result = 0
var op = ThreadPool.RunAsync(delegate { result = Compute() })
</font> op.Completed = <font color="#0000ff">delegate</font>(<font color="#4bacc6">IAsyncAction</font> asyncAction, <font color="#4bacc6">AsyncStatus</font> asyncStatus)
{
btnDoWork.Content = result.ToString() <font color="#9bbb59"><font color="#26ae31">// bug!</font>
</font> }
}</pre>

Now when the asynchronous operation queued to the ThreadPool completes, our Completed handler is invoked and tries to store the result into our button. Unfortunately, this is currently broken. The Completed handler is unlikely to be invoked on the UI thread, and yet, to modify btnDoWork.Content, the handler needs to be running on the UI thread (if it doesn’t, an exception with the error code RPC_E_WRONG_THREAD will result). To deal with this, we can use the CoreDispatcher object associated with our UI to marshal the invocation back to where we need it to be:

<font color="#a5a5a5">private void btnDoWork_Click(object sender, RoutedEventArgs e)
{
int result = 0
var op = ThreadPool.RunAsync(delegate { result = Compute() })
op.Completed = delegate(IAsyncAction asyncAction, AsyncStatus asyncStatus)
{
</font> Dispatcher.RunAsync(<font color="#4bacc6">CoreDispatcherPriority</font>.Normal, <font color="#0000ff">delegate</font>
{
<font color="#a5a5a5">btnDoWork.Content = result.ToString()</font>
})
<font color="#a5a5a5">}
}</font></pre>

This is now functional. But what if the Compute method throws an exception? Or what if someone calls Cancel on the IAsyncAction returned from ThreadPool.RunAsync? Our Completed handler needs to deal with the fact that the IAsyncAction may end in one of three terminal states: Completed, Error, or Canceled:

<font color="#a5a5a5">private void btnDoWork_Click(object sender, RoutedEventArgs e)
{
int result = 0
var op = ThreadPool.RunAsync(delegate { result = Compute() })
op.Completed = delegate(IAsyncAction asyncAction, AsyncStatus asyncStatus)
{
Dispatcher.RunAsync(CoreDispatcherPriority.Normal, delegate
{
</font> <font color="#0000ff">switch</font> (asyncStatus)
{
<font color="#0000ff">case</font> <font color="#4bacc6">AsyncStatus</font>.Completed:
<font color="#a5a5a5">btnDoWork.Content = result.ToString()</font>
<font color="#0000ff">break</font>
<font color="#0000ff">case</font> <font color="#4bacc6">AsyncStatus</font>.Error:
btnDoWork.Content = asyncAction.ErrorCode.Message
<font color="#0000ff">break</font>
<font color="#0000ff">case</font> <font color="#4bacc6">AsyncStatus</font>.Canceled:
btnDoWork.Content = "<font color="#c0504d">A task was canceled</font>"
<font color="#0000ff">break</font>
}
<font color="#a5a5a5">})
}
}</font></pre>

That’s a fair amount of code to write to handle a single asynchronous invocation imagine what it would look like if we needed to perform many asynchronous operations in sequence? Wouldn’t it be nice if we could instead write code like this?

private <font color="#0000ff">async</font> void btnDoWork_Click(object sender, <font color="#4bacc6">RoutedEventArgs</font> e)
{
try
{
int result = 0
<font color="#0000ff">await</font> <font color="#4bacc6">ThreadPool</font>.RunAsync(delegate { result = Compute() })
btnDoWork.Content = result.ToString()
}
catch (<font color="#4bacc6">Exception</font> exc) { btnDoWork.Content = exc.Message }
}</pre>

This code behaves exactly like the previous code. But we don’t need to deal manually with completion callbacks. We don’t need to marshal back manually to the UI thread. We don’t need to explicitly check completion status. And we don’t need to invert our control flow, which means this is now trivial to extend with more operations, e.g. to do multiple computations and UI updates in a loop:

<font color="#a5a5a5">private async void btnDoWork_Click(object sender, RoutedEventArgs e)
{
try
{
</font> <font color="#0000ff">for</font>(<font color="#0000ff">int</font> i=0 i<5 i++)
{
<font color="#a5a5a5">int result = 0
await ThreadPool.RunAsync(delegate { result = Compute() })
btnDoWork.Content = result.ToString()</font>
}
<font color="#a5a5a5">}
catch (Exception exc) { btnDoWork.Content = exc.Message }
}</font></pre>

Take just a moment to think through the code that you would have had to write to achieve that using the IAsyncAction manually. This is the magic of the new async/await keywords in C# and Visual Basic. The good news is that you can in fact write this exact code, and it’s not actually magic. Through the rest of this post, we explore exactly how this works behind the scenes.

Compiler transformations

Marking a method with the async keyword causes the C# or Visual Basic compiler to rewrite the method’s implementation using a state machine. Using this state machine the compiler can insert points into the method at which the method can suspend and resume its execution without blocking a thread. These points aren’t inserted haphazardly. They’re inserted only where you explicitly use the await keyword:

private async void btnDoWork_Click(object sender, <font color="#4bacc6">RoutedEventArgs</font> e)
{
...
<font color="#0000ff">await</font> something // <-- potential method suspension point
...
}</pre>

When you await an asynchronous operation that’s not yet completed, the compiler’s generated code ensures that all of the state associated with the method (e.g. local variables) is packaged up and preserved on the heap. Then the function returns to the caller, allowing the thread on which it was running to do other work. When the awaited asynchronous operation later completes, the method’s execution resumes using the preserved state.

Any type that exposes the await pattern can be awaited. The pattern consists primarily of exposing a GetAwaiter method that returns a type that provides IsCompleted, OnCompleted, and GetResult members. When you write:

<font color="#0000ff">await</font> something</pre>

the compiler generates code that uses these members on the instance something to check whether the object is already completed (via IsCompleted), and if it’s not completed, to hook up a continuation (via OnCompleted) that calls back to continue execution when the task eventually completes. After the operation is completed, any exceptions from the operation are propagated and/or a result returned (via GetResult). So, when you write this:

private <font color="#0000ff">async</font> <font color="#0000ff">void</font> btnDoWork_Click(object sender, <font color="#4bacc6">RoutedEventArgs</font> e)
{
...
<font color="#0000ff">await </font><font color="#4bacc6">ThreadPool</font>.RunAsync(delegate { result = Compute() })
...
}</pre>

the compiler translates that into code similar to this:

private class btnDoWork_ClickStateMachine : <font color="#4bacc6">IAsyncStateMachine</font>
{
// Member fields for preserving locals and other necessary state
int $state
<font color="#4bacc6">TaskAwaiter</font><string> $awaiter
int result
...
// Method that moves to the next state in the state machine
public void MoveNext()
{
// Jump table to get back to the right statement upon resumption
switch (this.$state)
{
...
case 2: goto Label2
...
}
...
// Expansion of await ...
var tmp1 = <font color="#a5a5a5">ThreadPool.RunAsync(<font color="#a5a5a5">delegate</font> { this.result = Compute() })
</font> this.$awaiter = tmp1.GetAwaiter()
if (!this.$awaiter.IsCompleted)
{
this.$state = 2
this.$awaiter.OnCompleted(MoveNext)
return
Label2:
}
this.$awaiter.GetResult()
...
}
}</pre>

For the method btnDoWork_Click, the compiler generates a state machine class that contains a MoveNext method. Every call to MoveNext resumes the execution of the btnDoWork_Click method until it reaches the next await on something that’s not yet completed, or until the end of the method, whichever comes first. When the compiler-generated code finds an awaited instance that’s not yet completed, it marks the current location with a state variable, schedules execution of the method to continue when the awaited instance completes, and then returns. When the awaited instance eventually completes, the MoveNext method is invoked again and jumps to the point in the method where execution previously left off.

The compiler doesn’t actually care that an IAsyncAction is being awaited here. All it cares about is that the right pattern is available with which to bind. Of course, you’ve seen what the IAsyncAction interface looks like, and you’ve seen that it doesn’t contain a GetAwaiter method like that expected by the compiler. So how is it that this successfully compiles and runs? To best understand that, you first need to understand the .NET Task and Task types (the Framework’s core representation of asynchronous operations), and how they relate to await.

Converting to tasks

The .NET Framework 4.5 includes all the types and methods necessary to support awaiting Task and Task instances (Task derives from Task). Task and Task both expose GetAwaiter instance methods, which respectively return TaskAwaiter and TaskAwaiter types that expose the necessary IsCompleted, OnCompleted, and GetResult members sufficient to satisfy the C# and Visual Basic compilers. IsCompleted returns a Boolean indicating whether the task is done executing as of the moment the property is accessed. OnCompleted hooks up to the task a continuation delegate that is invoked when the task completes (if the task is already completed when OnCompleted is invoked, the continuation delegate is scheduled for execution asynchronously). GetResult returns the result of the task if it ended in the TaskStatus.RanToCompletion state (it returns void for the non-generic Task type), throws an OperationCanceledException if the task ended in the TaskStatus.Canceled state, and throws whatever exception caused the task to fail if the task ended in the TaskStatus.Faulted state.

If we have a custom type that we want to support awaiting, we have two primary options. One option is to implement the whole await pattern manually for our custom awaitable type, providing a GetAwaiter method that returns a custom awaiter type that knows how to deal with continuations and exception propagation and the like. The second is to implement the ability to convert from our custom type to a task, and then just rely on the built-in support for awaiting tasks to await our special type. Let’s explore this latter approach.

The .NET Framework includes a type called TaskCompletionSource, which makes these kinds of conversions straightforward. TaskCompletionSource creates a Task object and gives you SetResult, SetException, and SetCanceled methods that you use to directly control when and in what state the corresponding task completes. So, you can use a TaskCompletionSource as a kind of shim or proxy to represent some other asynchronous operation, such as a WinRT asynchronous operation.

Let’s pretend for a moment that you didn’t already know you could directly await WinRT operations. How then could you enable doing so?

<font color="#4bacc6">IAsyncOperation</font><string> op = SomeMethodAsync()
string result = <font color="#0000ff">await </font>... // need something to await</pre>

You could create a TaskCompletionSource, use that as a proxy to represent the WinRT async operation, and then await the corresponding task. Let’s try. First, we need to instantiate a TaskCompletionSource such that we can await its Task:

<font color="#a5a5a5">IAsyncOperation<</font><font color="#a5a5a5">string> op = SomeMethodAsync()</font>
var tcs = new <font color="#4bacc6">TaskCompletionSource</font>()
...
<font color="#a5a5a5">string result = await</font> tcs.Task</pre>

Then, just as we saw in our earlier example of manually using WinRT async operations’ Completed handlers, we need to hook up a callback to the async operation so that we know when it completes:

<font color="#a5a5a5">IAsyncOperation<string> op = SomeMethodAsync()</font><font color="#a5a5a5">var tcs = new TaskCompletionSource()</font>op.Completed = delegate
{
<font color="#a5a5a5"> ...</font>
}
<font color="#a5a5a5">string result = await tcs.Task</font></pre>

And then in that callback, we need to transfer the IAsyncOperation’s completion state over to the task:

<font color="#a5a5a5">IAsyncOperation<<font color="#a5a5a5">string</font>> op = SomeMethodAsync()
<font color="#a5a5a5">var</font> tcs = <font color="#a5a5a5">new</font> TaskCompletionSource()
op.Completed = <font color="#a5a5a5">delegate</font>
{
</font> switch(operation.Status)
{
<font color="#4bacc6">AsyncStatus</font>.Completed:
tcs.SetResult(operation.GetResults())
break
<font color="#4bacc6">AsyncStatus</font>.Error:
tcs.SetException(operation.ErrorCode)
break
<font color="#4bacc6">AsyncStatus</font>.Canceled:
tcs.SetCanceled()
break
}
}
<font color="#a5a5a5"><font color="#a5a5a5">string</font> result = await tcs.Task</font></pre>

That’s it. WinRT async operations ensure that the Completed handler is invoked appropriately even if the handler is signed up after the operation has already completed, so we don’t need to do anything special for signing up the handler racing with the operation’s completion. WinRT async operations also take care to drop the reference to the Completed handler after the operation has completed, so we don’t need to do anything special to set Completed to null when our handler is invoked in fact, the Completed handler is set-once, meaning that after you set it, you get an error if you try to set it again.

With this approach, there’s a one-to-one mapping between how the state in which the WinRT async operation completes and the state in which the representing task completes:


<tbody>


Terminal AsyncStatus





Converts to TaskStatus





Which when awaited...





Completed





RanToCompletion





Returns the operation’s result (or void)











Error





Faulted





Throws the failed operation’s exception











Canceled





Canceled





Throws an OperationCanceledException


</tbody>


Of course, the boilerplate code we wrote to handle this one await would become tedious very quickly if we had to write it every time we wanted to await a WinRT asynchronous operation. As good programmers, we can encapsulate that boilerplate into a method we can use over and over again. Let’s do so as an extension method that converts the WinRT async operation into a task:

public static <font color="#4bacc6">Task</font> AsTask(
this <font color="#4bacc6">IAsyncOperation</font> operation)
{
<font color="#a5a5a5"><font color="#a5a5a5">var</font> tcs = <font color="#a5a5a5">new</font> TaskCompletionSource()
operation.Completed = delegate<font color="#a5a5a5">
</font> {
<font color="#a5a5a5">switch</font>(operation.Status)
{
AsyncStatus.Completed:
tcs.SetResult(operation.GetResults())
<font color="#a5a5a5">break</font>
AsyncStatus.Error:
tcs.SetException(operation.ErrorCode)
<font color="#a5a5a5">break</font>
AsyncStatus.Canceled:
tcs.SetCanceled()
<font color="#a5a5a5">break</font>
}
}
</font> return <font color="#a5a5a5">tcs.Task</font>
}</pre>

With that extension method, I can now write code like:

<font color="#4bacc6">IAsyncOperation</font><string> op = SomeMethodAsync()
string result = <font color="#0000ff">await</font> op.AsTask()</pre>

or even more simply as:

string result = <font color="#0000ff">await</font> SomeMethodAsync().AsTask()</pre>

Much better. Of course, this kind of cast-like AsTask functionality will be in hot demand by anyone using WinRT from C# and Visual Basic, so you don’t actually need to write your own implementation: there are already such methods built into .NET 4.5. The System.Runtime.WindowsRuntime.dll assembly contains these extension methods for the WinRT async interfaces:

namespace System
{
public static class <font color="#4bacc6">WindowsRuntimeSystemExtensions
</font> {
// IAsyncAction

public static <font color="#4bacc6">Task</font> AsTask(
this <font color="#4bacc6">IAsyncAction</font> source)
public static <font color="#4bacc6">Task</font> AsTask(
this <font color="#4bacc6">IAsyncAction</font> source,
<font color="#4bacc6">CancellationToken</font> cancellationToken)

// IAsyncActionWithProgress

public static <font color="#4bacc6">Task</font> AsTask<TProgress>(
this <font color="#4bacc6">IAsyncActionWithProgress</font><TProgress> source)
public static <font color="#4bacc6">Task</font> AsTask<TProgress>(
this <font color="#4bacc6">IAsyncActionWithProgress</font><TProgress> source,
<font color="#4bacc6">IProgress</font><TProgress> progress)
public static <font color="#4bacc6">Task</font> AsTask<TProgress>(
this <font color="#4bacc6">IAsyncActionWithProgress</font><TProgress> source,
<font color="#4bacc6">CancellationToken</font> cancellationToken)
public static <font color="#4bacc6">Task</font> AsTask<TProgress>(
this <font color="#4bacc6">IAsyncActionWithProgress</font><TProgress> source,
<font color="#4bacc6">CancellationToken</font> cancellationToken,
<font color="#4bacc6">IProgress</font><TProgress> progress)

// IAsyncOperation

public static <font color="#4bacc6">Task</font> AsTask(
this <font color="#4bacc6">IAsyncOperation</font> source)
public static <font color="#4bacc6">Task</font> AsTask(
this <font color="#4bacc6">IAsyncOperation</font> source,
<font color="#4bacc6">CancellationToken</font> cancellationToken)

// IAsyncOperationWithProgress

public static <font color="#4bacc6">Task</font> AsTask(
this <font color="#4bacc6">IAsyncOperationWithProgress</font> source)
public static <font color="#4bacc6">Task</font> AsTask(
this <font color="#4bacc6">IAsyncOperationWithProgress</font> source,
<font color="#4bacc6">IProgress</font><TProgress> progress)
public static <font color="#4bacc6">Task</font> AsTask(
this I<font color="#4bacc6">AsyncOperationWithProgress</font> source,
<font color="#4bacc6">CancellationToken</font> cancellationToken)
public static <font color="#4bacc6">Task</font> AsTask(
this <font color="#4bacc6">IAsyncOperationWithProgress</font> source,
<font color="#4bacc6">CancellationToken</font> cancellationToken,
<font color="#4bacc6">IProgress</font><TProgress> progress)

...
}
}</pre>

Each of the four interfaces has a parameterless AsTask overload similar to the one we just wrote from scratch. In addition, each also has an overload that accepts a CancellationToken. This token is the common mechanism in .NET used to provide composable and cooperative cancellation you pass a token into all of your asynchronous operations, and when cancellation is requested, all of those async operations will have cancellation requested. Just for illustration (because as you now know such an API is already available), how might we build our own such AsTask(CancellationToken) overload? CancellationToken provides a Register method that accepts a delegate to be invoked when cancellation is requested we can simply provide a delegate then that calls Cancel on the IAsyncInfo object, forwarding along the cancellation request:

public static <font color="#4bacc6">Task</font> AsTask(
this <font color="#4bacc6">IAsyncOperation</font> operation,
<font color="#4bacc6">CancellationToken</font> cancellationToken
{
using(cancellationToken.Register(() => operation.Cancel()))
return <font color="#0000ff">await</font> operation.AsTask()
}</pre>

Although the implementation that ships in .NET 4.5 isn’t exactly like this, logically it’s the same.

For IAsyncActionWithProgress<TProgress> and IAsyncOperationWithProgress, there are also overloads that accept an IProgress<TProgress>. IProgress<T> is a .NET interface that methods can accept to report back progress, and the AsTask method simply wires up a delegate for the WinRT async operation’s Progress property so that it forwards the progress info to the IProgress. Again, just as an example for how this might be implemented manually:

public static <font color="#4bacc6">Task</font> AsTask(
this <font color="#4bacc6">IAsyncOperationWithProgress</font> operation,
<font color="#4bacc6">IProgress</font><TProgress> progress
{
operation.Progress += (_,p) => progress.Report(p)
return operation.AsTask()
}</pre>

Directly awaiting WinRT async

We’ve now seen how it’s possible to create tasks to represent WinRT async operation such that we can then await those tasks. But what about directly awaiting the WinRT operations? In other words, it’s fine to be able to write:

<font color="#0000ff">await</font> SomeMethodAsync().AsTask()</pre>

but for cases where we don’t need to supply a CancellationToken or an IProgress<T>, wouldn’t it be nice to avoid coding the call to AsTask at all?

<font color="#0000ff">await</font> SomeMethodAsync()</pre>

Of course, this is possible, as we have seen at the beginning of this post. Remember how the compiler expects to find a GetAwaiter method that returns an appropriate awaiter type? The aforementioned WindowsRuntimeSystemExtensions type in System.Runtime.WindowsRuntime.dll includes just such GetAwaiter extension methods for the four WinRT async interfaces:

namespace System
{
public static class <font color="#4bacc6">WindowsRuntimeSystemExtensions</font>
{
...
public static <font color="#4bacc6">TaskAwaiter</font> GetAwaiter(
this <font color="#4bacc6">IAsyncAction</font> source)
public static <font color="#4bacc6">TaskAwaiter</font> GetAwaiter(
this <font color="#4bacc6">IAsyncOperation</font> source)
public static <font color="#4bacc6">TaskAwaiter</font> GetAwaiter<TProgress>(
this <font color="#4bacc6">IAsyncActionWithProgress</font><TProgress> source)
public static <font color="#4bacc6">TaskAwaiter</font> GetAwaiter(
this <font color="#4bacc6">IAsyncOperationWithProgress</font> source)
}
}</pre>

Note the return type from each of these methods: TaskAwaiter or TaskAwaiter. Each of these methods is taking advantage of the existing task awaiters built into the Framework. Knowing what you now know about AsTask, you can probably guess how these are implemented. The real implementation in the Framework is almost exactly like this:

public static <font color="#4bacc6">TaskAwaiter</font> GetAwaiter(
this <font color="#4bacc6">IAsyncAction </font>source)
{
return source.AsTask().GetAwaiter()
}</pre>

This means that these two lines both result in exactly the same behavior:

<font color="#0000ff">await</font> SomeMethodAsync().AsTask()
<font color="#0000ff">await</font> SomeMethodAsync()</pre>

Customizing await behavior

As mentioned previously, TaskAwaiter and TaskAwaiter supply all of the members necessary to meet the compiler’s expectation of an awaiter:

bool IsCompleted { <font color="#0000ff">get</font> }
void OnCompleted(<font color="#4bacc6">Action</font> continuation)
TResult GetResult() //returns void on TaskAwaiter</pre>

The most interesting member here is OnCompleted, as it’s the one responsible for invoking the continuation delegate when the awaited operation completes. OnCompleted provides special marshaling behavior to ensure that the continuation delegate is executed in the right place.

By default, when the task’s awaiter’s OnCompleted is called, it notes the current SynchronizationContext, which is an abstract representation of the environment in which the code is executing. On the UI thread of a Metro style app, SynchronizationContext.Current returns an instance of the internal WinRTSynchronizationContext type. SynchronizationContext provides a virtual Post method that accepts a delegate and executes that delegate in an appropriate place for the context WinRTSynchronizationContext wraps a CoreDispatcher and uses its RunAsync to invoke the delegate back on the UI thread asynchronously (just as we manually did earlier in this post). When the awaited task completes, the delegate passed to OnCompleted is Post’ed for execution to the captured SynchronizationContext that was current when OnCompleted was invoked. This is what allows you to write code using await in your UI logic without worrying about marshaling back to the right thread: task’s awaiter is handling it for you.

Of course, there may be situations in which you don’t want this default marshaling behavior. Such situations occur frequently in libraries: many kinds of libraries don’t care about manipulating UI controls or the particular threads on which they execute, and thus from a performance perspective, it’s helpful to be able to avoid the overhead associated with cross-thread marshaling. To accommodate code that wants to disable this default marshaling behavior, Task and Task provide ConfigureAwait methods. ConfigureAwait accepts a Boolean continueOnCapturedContext parameter: passing true means to use the default behavior, and passing false means that the system doesn’t need to forcefully marshal the delegate’s invocation back to the original context and can instead execute the delegate wherever the system sees fit.

Given that, if you want to await a WinRT operation without forcing the rest of the execution back to the UI thread, instead of writing either:

<font color="#0000ff">await</font> SomeMethodAsync()</pre>

or:

<font color="#0000ff">await</font> SomeMethodAsync().AsTask()</pre>

you can write:

<font color="#0000ff">await</font> SomeMethodAsync().AsTask()
.ConfigureAwait(continueOnCapturedContext:false)</pre>

or just:

<font color="#0000ff">await</font> SomeMethodAsync().AsTask().ConfigureAwait(false)</pre>

When to use AsTask

If all you want to do is invoke a WinRT asynchronous operation and wait for it to complete, directly awaiting the WinRT asynchronous operation is the simplest and cleanest approach:

<font color="#0000ff">await</font> SomeMethodAsync()</pre>

But as soon as you want more control, you’ll need to use AsTask. You’ve already seen a few such cases where this is useful:

  • Supporting cancellation via CancellationToken

    <font color="#4bacc6">CancellationToken </font>token = ...
    <font color="#0000ff">await</font> SomeMethodAsync().AsTask(token)</pre>

  • Supporting progress reporting via IProgress<T>

    <font color="#4bacc6">IProgress</font><TProgress> progress = ...
    <font color="#0000ff">await</font> SomeMethodAsync().AsTask(progress)</pre>

  • Suppressing the default continuation marshaling behavior via ConfigureAwait

    <font color="#0000ff">await </font>SomeMethodAsync().AsTask().ConfigureAwait(false)</pre>

There are also additional important situations where AsTask can be quite useful.

One situation has to do with Task’s ability to support multiple continuations. The WinRT async operation types support only a single delegate registered with Completed (Completed is a property rather than an event), and it may be set only once. This is fine for the majority of cases where you simply want to await the operation once, e.g. instead of calling a synchronous method:

SomeMethod()</pre>

you call and await an asynchronous counterpart:

<font color="#0000ff">await</font> SomeMethodAsync()</pre>

logically maintaining the same control flow as if you used the synchronous counterpart. But sometimes you want to be able to hook up multiple callbacks, or you want to be able to await the same instance multiple times. In contrast to the WinRT async interfaces, the Task type does support being awaited any number of times and/or having its ContinueWith method being used any number of times to support any number of callbacks. Thus, you can use AsTask to get a task for your WinRT async operation, and then hook up your multiple callbacks to the Task rather than to the WinRT async operation directly.

var t = SomeMethodAsync().AsTask()
t.ContinueWith(delegate { ... })
t.ContinueWith(delegate { ... })
t.ContinueWith(delegate { ... })</pre>

Another example where AsTask can be useful is when dealing with methods that operate in terms of the Task or Task types. Combinator methods like Task.WhenAll or Task.WhenAny operate in terms of Task, not in terms of the WinRT async interfaces. So, if you wanted to be able to invoke multiple WinRT async operations and then await for all or any of them to complete, you could use AsTask to make this easy. For example, this await completes as soon as any of the three supplied operations complete, and returns the Task representing whichever it was:

<font color="#4bacc6">Task</font> firstCompleted = <font color="#0000ff">await </font>Task.WhenAny(
SomeMethod1Async().AsTask(),
SomeMethod2Async().AsTask(),
SomeMethod3Async().AsTask())</pre>

Conclusion

It’s truly exciting how much functionality WinRT via asynchronous operations provide the volume of such APIs exposed speaks to just how important responsiveness is to the platform. This in turn places a significant demand on the programming model used to work with these operations: for C# and Visual Basic code, await and AsTask rise to the occasion. Hopefully this blog post has peeled back the curtain sufficiently to give you a good sense of exactly how these capabilities work and enable you to productively develop Metro style apps.

For more info, I recommend these resources:

  • [url=http://msdn.microsoft.com/en-us/async">Visual Studio Async Programming</a>
  • [url=http://blogs.msdn.com/b/pfxteam/archive/2012/04/12/10293335.aspx">Async/Await FAQ</a>
  • [url=http://channel9.msdn.com/events/BUILD/BUILD2011/TOOL-810T">Async made simple in Windows 8, with C# and Visual Basic</a>
    [/list]

    Stephen Toub

    Visual Studio

    [url=http://blogs.msdn.com/b/windowsappdev/archive/2012/04/24/diving-deep-with-winrt-and-await.aspx]View the full article
 
Last edited:
Back
Top