Wednesday 11 February 2015

ASP.NET Tips #23 - Want to build scalable websites and services? Work asynchronously

One of the secrets to producing scalable websites and services is to perform all your I/O operations asynchronously to avoid blocking threads.

When your thread issues a synchronous I/O request, the Windows kernel blocks the thread. This causes the thread pool to create a new thread, which allocates a lot of memory and wastes precious CPU time. Calling xxxAsync method and using C#'s async/await keywords allows your thread to return to the thread pool so it can be used for other things. This reduces the resource consumption of your app, allowing it to use more memory and improving response time to your clients.

Take advantage of .NET 4.5 async/await constructs

With the arrival of .NET 4.5, writing async/await code correctly is easier than ever. Like any tool, it should be only applied where it makes most sense – in web use-cases this usually revolves around I/O operations (i.e. reading from disk, any network operation, database operations, sending email, file transfer over FTP or calls to web services).

Be careful of variable allocations

Inside async methods, .NET will create a state machine behind the scenes to lift out local variables for you. That way, when the method resumes, all the values are still there. It's a fantastic feature, but at it's not yet smart enough to tell if you still need a particular variable.

Consider this code example:

var today = DateTime.Now;
var tomorrow = today.AddDays(1);
await MyTaskHere();

In this example we have two DateTime variables declared - yes, it’s a trivial example, but it proves the point. After the await, we only need to use the "tomorrow" value. However, the state machine will lift out both the today and tomorrow variables. In this example, the object that is used to retain state for the async operation is larger than necessary. This means more variable declarations, and eventually this can lead to additional GC cycles, and so on.

To avoid this, and reduce the size of the state-tracking object, you could re-write the code like so:

var tomorrow = DateTime.Now.AddDays(1);
await MyTaskHere();

By being smart with the variables that you have inside an async method, you can help control the size of the state-tracking object.

Using the keyword await doesn't make the work asynchronous

The async/await pattern makes it easy to write code that depends on work that's done asynchronously, but it doesn't turn synchronous code into asynchronous code.

If you await an async Task method that does some work and returns a value, the work will be done synchronously – the original method is waiting for a Task object to be returned which it can then await on, but the only Task it gets is one which is wrapped around the return value and set to “completed”. This is useful if you are chaining async methods, but in this case it is confusing.

By the time the awaiting method sees the Task, it's already complete, so the code continues to execute synchronously. If you want to do work asynchronously, you need to pass back a Task object representing the work in progress. One way of doing this is by using Task.Run().

Don’t use async/await for short methods

Async/await is great for avoiding blocking while potentially time-consuming work is performed, but there are overheads associated with running an async method: the current execution context has to be captured, there is a thread transition, and a state machine is built through which your code runs. The cost of this is comparatively negligible when the asynchronous work takes a long time, but it's worth keeping in mind.

Avoid using async/await for very short methods or having await statements in tight loops (run the whole loop asynchronously instead). Microsoft recommends that any method that might take longer than 50ms to return should run asynchronously, so you may wish to use this figure to determine whether it's worth using the async/await pattern.

Have a look at :

Never call .Wait() or .Result on a Task

Never call .Wait() or .Result on a Task within your application; it can bring your server crashing down. It shouldn't be necessary to mention this, but so many examples do this that it's worth reiterating.

No comments :

Post a Comment