Async Basics - Concurrency in C# Cookbook (2014)

Concurrency in C# Cookbook (2014)

Chapter 2. Async Basics

This chapter introduces you to the basics of using async and await for asynchronous operations. This chapter only deals with naturally asynchronous operations, which are operations such as HTTP requests, database commands, and web service calls.

If you have a CPU-intensive operation that you want to treat as though it were asynchronous (e.g., so it doesn’t block the UI thread), then see Chapter 3 and Recipe 7.4. Also, this chapter only deals with operations that are started once and complete once; if you need to handle streams of events, then see Chapter 5.

To use async on older platforms, install the NuGet package Microsoft.Bcl.Async into your application. Some platforms support async natively, and some should have the package installed (see Table 2-1):

Table 2-1. Platform support for async

Platform

Dataflow support

.NET 4.5

Yes

.NET 4.0

NuGet

Mono iOS/Droid

Yes

Windows Store

Yes

Windows Phone Apps 8.1

Yes

Windows Phone SL 8.0

Yes

Windows Phone 7.1

NuGet

Silverlight 5

NuGet

2.1. Pausing for a Period of Time

Problem

You need to (asynchronously) wait for a period of time. This can be useful when unit testing or implementing retry delays. This solution can also be useful for simple timeouts.

Solution

The Task type has a static method Delay that returns a task that completes after the specified time.

TIP

If you are using the Microsoft.Bcl.Async NuGet library, the Delay member is on the TaskEx type, not the Task type.

This example defines a task that completes asynchronously, for use with unit testing. When faking an asynchronous operation, it’s important to test at least synchronous success and asynchronous success as well as asynchronous failure. This example returns a task used for the asynchronous success case:

static async Task<T> DelayResult<T>(T result, TimeSpan delay)

{

await Task.Delay(delay);

return result;

}

This next example is a simple implementation of an exponential backoff, that is, a retry strategy where you increase the delays between retries. Exponential backoff is a best practice when working with web services to ensure the server does not get flooded with retries.

TIP

For production code, I would recommend a more thorough solution, such as the Transient Error Handling Block in Microsoft’s Enterprise Library; the following code is just a simple example of Task.Delay usage.

static async Task<string> DownloadStringWithRetries(string uri)

{

using (var client = new HttpClient())

{

// Retry after 1 second, then after 2 seconds, then 4.

var nextDelay = TimeSpan.FromSeconds(1);

for (int i = 0; i != 3; ++i)

{

try

{

return await client.GetStringAsync(uri);

}

catch

{

}

await Task.Delay(nextDelay);

nextDelay = nextDelay + nextDelay;

}

// Try one last time, allowing the error to propogate.

return await client.GetStringAsync(uri);

}

}

This final example uses Task.Delay as a simple timeout; in this case, the desired semantics are to return null if the service does not respond within three seconds:

static async Task<string> DownloadStringWithTimeout(string uri)

{

using (var client = new HttpClient())

{

var downloadTask = client.GetStringAsync(uri);

var timeoutTask = Task.Delay(3000);

var completedTask = await Task.WhenAny(downloadTask, timeoutTask);

if (completedTask == timeoutTask)

return null;

return await downloadTask;

}

}

Discussion

Task.Delay is a fine option for unit testing asynchronous code or for implementing retry logic. However, if you need to implement a timeout, a CancellationToken is usually a better choice.

See Also

Recipe 2.5 covers how Task.WhenAny is used to determine which task completes first. Recipe 9.3 covers using CancellationToken as a timeout.

2.2. Returning Completed Tasks

Problem

You need to implement a synchronous method with an asynchronous signature. This situation can arise if you are inheriting from an asynchronous interface or base class but wish to implement it synchronously. This technique is particularly useful when unit testing asynchronous code, when you need a simple stub or mock for an asynchronous interface.

Solution

You can use Task.FromResult to create and return a new Task<T> that is already completed with the specified value:

interface IMyAsyncInterface

{

Task<int> GetValueAsync();

}

class MySynchronousImplementation : IMyAsyncInterface

{

public Task<int> GetValueAsync()

{

return Task.FromResult(13);

}

}

TIP

If you’re using Microsoft.Bcl.Async, the FromResult method is on the TaskEx type.

Discussion

If you are implementing an asynchronous interface with synchronous code, avoid any form of blocking. It is not natural for an asynchronous method to block and then return a completed task. For a counterexample, consider the Console text readers in .NET 4.5.Console.In.ReadLineAsync will actually block the calling thread until a line is read, and then will return a completed task. This behavior is not intuitive and has surprised many developers. If an asynchronous method blocks, it prevents the calling thread from starting other tasks, which interferes with concurrency and may even cause a deadlock.

Task.FromResult provides synchronous tasks only for successful results. If you need a task with a different kind of result (e.g., a task that is completed with a NotImplementedException), then you can create your own helper method using TaskCompletionSource:

static Task<T> NotImplementedAsync<T>()

{

var tcs = new TaskCompletionSource<T>();

tcs.SetException(new NotImplementedException());

return tcs.Task;

}

Conceptually, Task.FromResult is just a shorthand for TaskCompletionSource, very similar to the preceding code.

If you regularly use Task.FromResult with the same value, consider caching the actual task. For example, if you create a Task<int> with a zero result once, then you avoid creating extra instances that will have to be garbage-collected:

private static readonly Task<int> zeroTask = Task.FromResult(0);

static Task<int> GetValueAsync()

{

return zeroTask;

}

See Also

Recipe 6.1 covers unit testing asynchronous methods.

Recipe 10.1 covers inheritance of async methods.

2.3. Reporting Progress

Problem

You need to respond to progress while an asynchronous operation is executing.

Solution

Use the provided IProgress<T> and Progress<T> types. Your async method should take an IProgress<T> argument; the T is whatever type of progress you need to report:

static async Task MyMethodAsync(IProgress<double> progress = null)

{

double percentComplete = 0;

while (!done)

{

...

if (progress != null)

progress.Report(percentComplete);

}

}

Calling code can use it as such:

static async Task CallMyMethodAsync()

{

var progress = new Progress<double>();

progress.ProgressChanged += (sender, args) =>

{

...

};

await MyMethodAsync(progress);

}

Discussion

By convention, the IProgress<T> parameter may be null if the caller does not need progress reports, so be sure to check for this in your async method.

Bear in mind that the IProgress<T>.Report method may be asynchronous. This means that MyMethodAsync may continue executing before the progress is actually reported. For this reason, it’s best to define T as an immutable type or at least a value type. If T is a mutable reference type, then you’ll have to create a separate copy yourself each time you call IProgress<T>.Report.

Progress<T> will capture the current context when it is constructed and will invoke its callback within that context. This means that if you construct the Progress<T> on the UI thread, then you can update the UI from its callback, even if the asynchronous method is invoking Reportfrom a background thread.

When a method supports progress reporting, it should also make a best effort to support cancellation.

See Also

Recipe 9.4 covers how to support cancellation in an asynchronous method.

2.4. Waiting for a Set of Tasks to Complete

Problem

You have several tasks and need to wait for them all to complete.

Solution

The framework provides a Task.WhenAll method for this purpose. This method takes several tasks and returns a task that completes when all of those tasks have completed:

Task task1 = Task.Delay(TimeSpan.FromSeconds(1));

Task task2 = Task.Delay(TimeSpan.FromSeconds(2));

Task task3 = Task.Delay(TimeSpan.FromSeconds(1));

await Task.WhenAll(task1, task2, task3);

If all the tasks have the same result type and they all complete successfully, then the Task.WhenAll task will return an array containing all the task results:

Task task1 = Task.FromResult(3);

Task task2 = Task.FromResult(5);

Task task3 = Task.FromResult(7);

int[] results = await Task.WhenAll(task1, task2, task3);

// "results" contains { 3, 5, 7 }

There is an overload of Task.WhenAll that takes an IEnumerable of tasks; however, I do not recommend that you use it. Whenever I mix asynchronous code with LINQ, I find the code is clearer when I explicitly “reify” the sequence (i.e., evaluate the sequence, creating a collection):

static async Task<string> DownloadAllAsync(IEnumerable<string> urls)

{

var httpClient = new HttpClient();

// Define what we're going to do for each URL.

var downloads = urls.Select(url => httpClient.GetStringAsync(url));

// Note that no tasks have actually started yet

// because the sequence is not evaluated.

// Start all URLs downloading simultaneously.

Task<string>[] downloadTasks = downloads.ToArray();

// Now the tasks have all started.

// Asynchronously wait for all downloads to complete.

string[] htmlPages = await Task.WhenAll(downloadTasks);

return string.Concat(htmlPages);

}

TIP

If you are using the Microsoft.Bcl.Async NuGet library, the WhenAll member is on the TaskEx type, not the Task type.

Discussion

If any of the tasks throws an exception, then Task.WhenAll will fault its returned task with that exception. If multiple tasks throw an exception, then all of those exceptions are placed on the Task returned by Task.WhenAll. However, when that task is awaited, only one of them will be thrown. If you need each specific exception, you can examine the Exception property on the Task returned by Task.WhenAll:

static async Task ThrowNotImplementedExceptionAsync()

{

throw new NotImplementedException();

}

static async Task ThrowInvalidOperationExceptionAsync()

{

throw new InvalidOperationException();

}

static async Task ObserveOneExceptionAsync()

{

var task1 = ThrowNotImplementedExceptionAsync();

var task2 = ThrowInvalidOperationExceptionAsync();

try

{

await Task.WhenAll(task1, task2);

}

catch (Exception ex)

{

// "ex" is either NotImplementedException or InvalidOperationException.

...

}

}

static async Task ObserveAllExceptionsAsync()

{

var task1 = ThrowNotImplementedExceptionAsync();

var task2 = ThrowInvalidOperationExceptionAsync();

Task allTasks = Task.WhenAll(task1, task2);

try

{

await allTasks;

}

catch

{

AggregateException allExceptions = allTasks.Exception;

...

}

}

Most of the time, I do not observe all the exceptions when using Task.WhenAll. It is usually sufficient to just respond to the first error that was thrown, rather than all of them.

See Also

Recipe 2.5 covers a way to wait for any of a collection of tasks to complete.

Recipe 2.6 covers waiting for a collection of tasks to complete and performing actions as each one completes.

Recipe 2.8 covers exception handling for async Task methods.

2.5. Waiting for Any Task to Complete

Problem

You have several tasks and need to respond to just one of them completing. The most common situation for this is when you have multiple independent attempts at an operation, with a first-one-takes-all kind of structure. For example, you could request stock quotes from multiple web services simultaneously, but you only care about the first one that responds.

Solution

Use the Task.WhenAny method. This method takes a sequence of tasks and returns a task that completes when any of the tasks complete. The result of the returned task is the task that completed. Don’t worry if that sounds confusing; it’s one of those things that’s difficult to explain but easy to demonstrate:

// Returns the length of data at the first URL to respond.

private static async Task<int> FirstRespondingUrlAsync(string urlA, string urlB)

{

var httpClient = new HttpClient();

// Start both downloads concurrently.

Task<byte[]> downloadTaskA = httpClient.GetByteArrayAsync(urlA);

Task<byte[]> downloadTaskB = httpClient.GetByteArrayAsync(urlB);

// Wait for either of the tasks to complete.

Task<byte[]> completedTask =

await Task.WhenAny(downloadTaskA, downloadTaskB);

// Return the length of the data retrieved from that URL.

byte[] data = await completedTask;

return data.Length;

}

TIP

If you are using the Microsoft.Bcl.Async NuGet library, the WhenAny member is on the TaskEx type, not the Task type.

Discussion

The task returned by Task.WhenAny never completes in a faulted or canceled state. It always results in the first Task to complete; if that task completed with an exception, then the exception is not propogated to the task returned by Task.WhenAny. For this reason, you should usuallyawait the task after it has completed.

When the first task completes, consider whether to cancel the remaining tasks. If the other tasks are not canceled but are also never awaited, then they are abandoned. Abandoned tasks will run to completion, and their results will be ignored. Any exceptions from those abandoned tasks will also be ignored.

It is possible to use Task.WhenAny to implement timeouts (e.g., using Task.Delay as one of the tasks), but it’s not recommended. It’s more natural to express timeouts with cancellation, and cancellation has the added benefit that it can actually cancel the operation(s) if they time out.

Another antipattern for Task.WhenAny is handling tasks as they complete. At first it seems like a reasonable approach to keep a list of tasks and remove each task from the list as it completes. The problem with this approach is that it executes in O(N^2) time, when an O(N) algorithm exists. The proper O(N) algorithm is discussed in Recipe 2.6.

See Also

Recipe 2.4 covers asynchronously waiting for all of a collection of tasks to complete.

Recipe 2.6 covers waiting for a collection of tasks to complete and performing actions as each one completes.

Recipe 9.3 covers using a cancellation token to implement timeouts.

2.6. Processing Tasks as They Complete

Problem

You have a collection of tasks to await, and you want to do some processing on each task after it completes. However, you want to do the processing for each one as soon as it completes, not waiting for any of the other tasks.

As an example, this is some code that kicks off three delay tasks and then awaits each one:

static async Task<int> DelayAndReturnAsync(int val)

{

await Task.Delay(TimeSpan.FromSeconds(val));

return val;

}

// Currently, this method prints "2", "3", and "1".

// We want this method to print "1", "2", and "3".

static async Task ProcessTasksAsync()

{

// Create a sequence of tasks.

Task<int> taskA = DelayAndReturnAsync(2);

Task<int> taskB = DelayAndReturnAsync(3);

Task<int> taskC = DelayAndReturnAsync(1);

var tasks = new[] { taskA, taskB, taskC };

// Await each task in order.

foreach (var task in tasks)

{

var result = await task;

Trace.WriteLine(result);

}

}

The code currently awaits each task in sequence order, even though the second task in the sequence is the first one to complete. What we want is to do the processing (e.g., Trace.WriteLine) as each task completes without waiting for the others.

Solution

There are a few different approaches you can take to solve this problem. The one described first in this recipe is the recommended approach; another is described in the Discussion section.

The easiest solution is to restructure the code by introducing a higher-level async method that handles awaiting the task and processing its result. Once the processing is factored out, the code is significantly simplified:

static async Task<int> DelayAndReturnAsync(int val)

{

await Task.Delay(TimeSpan.FromSeconds(val));

return val;

}

static async Task AwaitAndProcessAsync(Task<int> task)

{

var result = await task;

Trace.WriteLine(result);

}

// This method now prints "1", "2", and "3".

static async Task ProcessTasksAsync()

{

// Create a sequence of tasks.

Task<int> taskA = DelayAndReturnAsync(2);

Task<int> taskB = DelayAndReturnAsync(3);

Task<int> taskC = DelayAndReturnAsync(1);

var tasks = new[] { taskA, taskB, taskC };

var processingTasks = (from t in tasks

select AwaitAndProcessAsync(t)).ToArray();

// Await all processing to complete

await Task.WhenAll(processingTasks);

}

Alternatively, this can be written as:

static async Task<int> DelayAndReturnAsync(int val)

{

await Task.Delay(TimeSpan.FromSeconds(val));

return val;

}

// This method now prints "1", "2", and "3".

static async Task ProcessTasksAsync()

{

// Create a sequence of tasks.

Task<int> taskA = DelayAndReturnAsync(2);

Task<int> taskB = DelayAndReturnAsync(3);

Task<int> taskC = DelayAndReturnAsync(1);

var tasks = new[] { taskA, taskB, taskC };

var processingTasks = tasks.Select(async t =>

{

var result = await t;

Trace.WriteLine(result);

}).ToArray();

// Await all processing to complete

await Task.WhenAll(processingTasks);

}

This refactoring is the cleanest and most portable way to solve this problem. However, it is subtly different than the original code. This solution will do the task processing concurrently, whereas the original code would do the task processing one at a time. Most of the time this is not a problem, but if it is not acceptable for your situation, then consider using locks (Recipe 11.2) or the following alternative solution.

Discussion

If refactoring the code like this is not a palatable solution, then there is an alternative. Stephen Toub and Jon Skeet have both developed an extension method that returns an array of tasks that will complete in order. Stephen Toub’s solution is available on the Parallel Programming with .NET blog, and Jon Skeet’s solution is available on his coding blog.

TIP

This extension method is also available in the open source AsyncEx library, available in the Nito.AsyncEx NuGet package.

Using an extension method like OrderByCompletion minimizes the changes to the original code:

static async Task<int> DelayAndReturnAsync(int val)

{

await Task.Delay(TimeSpan.FromSeconds(val));

return val;

}

// This method now prints "1", "2", and "3".

static async Task UseOrderByCompletionAsync()

{

// Create a sequence of tasks.

Task<int> taskA = DelayAndReturnAsync(2);

Task<int> taskB = DelayAndReturnAsync(3);

Task<int> taskC = DelayAndReturnAsync(1);

var tasks = new[] { taskA, taskB, taskC };

// Await each one as they complete.

foreach (var task in tasks.OrderByCompletion())

{

var result = await task;

Trace.WriteLine(result);

}

}

See Also

Recipe 2.4 covers asynchronously waiting for a sequence of tasks to complete.

2.7. Avoiding Context for Continuations

Problem

When an async method resumes after an await, by default it will resume executing within the same context. This can cause performance problems if that context was a UI context and a large number of async methods are resuming on the UI context.

Solution

To avoid resuming on a context, await the result of ConfigureAwait and pass false for its continueOnCapturedContext parameter:

async Task ResumeOnContextAsync()

{

await Task.Delay(TimeSpan.FromSeconds(1));

// This method resumes within the same context.

}

async Task ResumeWithoutContextAsync()

{

await Task.Delay(TimeSpan.FromSeconds(1)).ConfigureAwait(false);

// This method discards its context when it resumes.

}

Discussion

Having too many continuations run on the UI thread can cause a performance problem. This type of performance problem is difficult to diagnose, since it is not a single method that is slowing down the system. Rather, the UI performance begins to suffer from “thousands of paper cuts” as the application grows more complex.

The real question is, how many continuations on the UI thread are too many? There is no hard-and-fast answer, but Lucian Wischik of Microsoft has publicized the guideline used by the WinRT team: a hundred or so per second is OK, but a thousand or so per second is too many.

It’s best to avoid this right at the beginning. For every async method you write, if it doesn’t need to resume to its original context, then use ConfigureAwait. There’s no disadvanage to doing so.

It’s also a good idea to be context aware when writing async code. Normally, an async method should either require context (dealing with UI elements or ASP.NET requests/responses), or it should be free from context (doing background operations). If you have an async method that has parts requiring context and parts context free, consider splitting it up into two (or more) async methods. This helps keep your code better organized into layers.

See Also

Chapter 1 covers an introduction to asynchronous programming.

2.8. Handling Exceptions from async Task Methods

Problem

Exception handling is a critical part of any design. It’s easy to design for the success case but a design is not correct until it also handles the failure cases. Fortunately, handling exceptions from async Task methods is straightforward.

Solution

Exceptions can be caught by a simple try/catch, just like you would for synchronous code:

static async Task ThrowExceptionAsync()

{

await Task.Delay(TimeSpan.FromSeconds(1));

throw new InvalidOperationException("Test");

}

static async Task TestAsync()

{

try

{

await ThrowExceptionAsync();

}

catch (InvalidOperationException)

{

}

}

Exceptions raised from async Task methods are placed on the returned Task. They are only raised when the returned Task is awaited:

static async Task ThrowExceptionAsync()

{

await Task.Delay(TimeSpan.FromSeconds(1));

throw new InvalidOperationException("Test");

}

static async Task TestAsync()

{

// The exception is thrown by the method and placed on the task.

Task task = ThrowExceptionAsync();

try

{

// The exception is reraised here, where the task is awaited.

await task;

}

catch (InvalidOperationException)

{

// The exception is correctly caught here.

}

}

Discussion

When an exception is thrown out of an async Task method, that exception is captured and put on the returned Task. Since async void methods don’t have a Task to put their exception on, their behavior is different; we’ll cover that in another recipe.

When you await a faulted Task, the first exception on that task is rethrown. If you’re familiar with the problems of rethrowing exceptions, you may be wondering about stack traces. Rest assured: when the exception is rethrown, the original stack trace is correctly preserved.

This setup sounds somewhat complicated, but all this complexity works together so that the simple scenario has simple code. In the common case, your code should propagate exceptions from asynchronous methods that it calls; all it has to do is await the task returned from that asynchronous method, and the exception will be propagated naturally.

There are some situations (such as Task.WhenAll) where a Task may have multiple exceptions, and await will only rethrow the first one. See Recipe 2.4 for an example of handling all exceptions.

See Also

Recipe 2.4 covers waiting for multiple tasks.

Recipe 2.9 covers techniques for catching exceptions from async void methods.

Recipe 6.2 covers unit testing exceptions thrown from async Task methods.

2.9. Handling Exceptions from async Void Methods

Problem

You have an async void method and need to handle exceptions propagated out of that method.

Solution

There is no good solution. If at all possible, change the method to return Task instead of void. In some situations, this isn’t possible; for example, let’s say you need to unit test an ICommand implementation (which must return void). In this case, you can provide a Task-returning overload of your Execute method as such:

sealed class MyAsyncCommand : ICommand

{

async void ICommand.Execute(object parameter)

{

await Execute(parameter);

}

public async Task Execute(object parameter)

{

... // Asynchronous command implementation goes here.

}

... // Other members (CanExecute, etc)

}

It’s best to avoid propagating exceptions out of async void methods. If you must use an async void method, consider wrapping all of its code in a try block and handling the exception directly.

There is another solution for handling exceptions from async void methods. When an async void method propagates an exception, that exception is raised on the SynchronizationContext that was active at the time the async void method started executing. If your execution environment provides a SynchronizationContext, then it usually has a way to handle these top-level exceptions at a global scope. For example, WPF has Application.DispatcherUnhandledException, WinRT has Application.UnhandledException, and ASP.NET hasApplication_Error.

It is also possible to handle exceptions from async void methods by controlling the SynchronizationContext. Writing your own SynchronizationContext is not trivial, but you can use the AsyncContext type from the free Nito.AsyncEx NuGet library. AsyncContext is particularly useful for applications that do not have a built-in SynchronizationContext, such as Console applications and Win32 services. The next example uses AsyncContext in a Console application; in this example, the async method does return Task, but AsyncContext also works for async void methods:

static class Program

{

static int Main(string[] args)

{

try

{

return AsyncContext.Run(() => MainAsync(args));

}

catch (Exception ex)

{

Console.Error.WriteLine(ex);

return -1;

}

}

static async Task<int> MainAsync(string[] args)

{

...

}

}

Discussion

One reason to prefer async Task over async void is that Task-returning methods are easier to test. At the very least, overloading void-returning methods with Task-returning methods will give you a testable API surface.

If you do need to provide your own SynchronizationContext type (such as AsyncContext), be sure not to install that SynchronizationContext on any threads that don’t belong to you. As a general rule, you should not place a SynchronizationContext on any thread that already has one (such as UI or ASP.NET request threads); nor should you place a SynchronizationContext on thread-pool threads. The main thread of a Console application does belong to you, and so do any threads you manually create yourself.

TIP

The AsyncContext type is in the Nito.AsyncEx NuGet package.

See Also

Recipe 2.8 covers exception handling with async Task methods.

Recipe 6.3 covers unit testing async void methods.