Async coding guidelines

Spread the love

Here are some basic guidelines for writing async C# code (.NET 4.5+). These are adapted from guidance I have given my colleagues and should be useful primarily to those new to Task, async/await, and the like. To keep things interesting, I have tried to add some justification to each guideline.

Use a return type of Task for a logically void method and Task<T> for a method returning a result.

Why? Well, you can’t really use the Task-based async pattern without it!

Use the naming convention XxxAsync, e.g. OpenAsync.

Why? It promotes consistency in your interfaces and helps you to avoid potential bugs simply by inspection, e.g.:

public void BadSyncMethod()
{
    Step1();
    Step2();
}

private async Task Step1()
{
    // ...
}

private void Step2()
{
    // ...
}

In the code above, Step1 is actually async and its returned Task is not observed by BadSyncMethod. The bug is very easy to spot if you use the standard naming convention, e.g.:

public void BadSyncMethod()
{
    Step1Async(); // clearly this is a dangling Task now
    Step2();
}

In multi-step async methods, use the ‘async’ and ‘await’ keywords.

Why? It is far easier to write async code using async/await than the pre-.NET 4.5 situation of chaining together ContinueWith methods. Pretty noncontroversial, as most people would want to do this without being asked. But…

In methods with exactly one async call and no post-execution steps, directly return the Task/Task<T> and do not mark ‘async’.

Why? An ‘async’ method is not free. The compiler does some complex code generation steps to morph your method into a proper asynchronous state machine with all the associated exception handling logic, management of continuation callbacks, etc. For a method which is not called very often, the cost would hardly matter. But for frequently hit code paths, there will some cost associated with setting up the async state machine and other costs incurred because the compiler will have fewer opportunities for inlining/optimization of such code. So if you can remove ‘async’ without complicating the code, go ahead and do it!

Avoid running blocking code in an asynchronous context.

Why? Blocking pretty much defeats the purpose of asynchrony. The caller is expecting your method to do a minimum of preparation and return back as soon as possible with a Task representing the ongoing background operation. If you block, you will make the calling thread less responsive (a bad thing if you are, say, in a UI context).

Leave a Reply

Your email address will not be published. Required fields are marked *