Wednesday 10 June 2015

Return Await vs Returning Task

I'm a true believe in trying to keep code as simple as possible by minimising the amount I have to write whilst not compromising functionality and readability.  I ran into a problem the other day when using the async/await pattern along with the Task Parallel Library (TPL).  Observe the following code snippet:

Task<int> CalculateValue()
{
    using (var calc = new Calculator())
    {
        return calc.Calculate();
    }
}

First thing to mention is I like to conform to standards - if my class is directly using unmanaged resources or any indirect unmanaged resource through another managed class then I like to implement the IDisposable pattern to ensure there are no memory leaks throughout the lifetime of the application.

So back to the problem at hand - the Calculate method on the Calculator class returns a Task<int> as this is a long running calculation which is done in another thread.  All is good except for the crucial part that upon the Calculate method being called the execution is returned back to the CalculateValue method whereby the Dispose method on the Calculator class is called long before the Calculate method completing.  The reason is simply because I didn't decorate the calc.Calculate call with an await and the CalculateValue method with an async keyword.

To resolve this problem I should change to the following:

async Task<int> CalculateValue()
{
    using (var calc = new Calculator())
    {
        return await calc.Calculate();
    }
}

So once the execution of the Task returned by the Calculate method is complete the Dispose method on the Calculator is subsequently called.

The reason I didn't implement it like this in the first place is because I don't like to add complexity to where I believe it isn't required.  Adding the await keyword on the calc.Calculate call would imply it has code to run after the call is complete - in my opinion it didn't have anything further to call apart from correctly disposing of the calculator which I easily missed.  Implementing the call this way would have been perfectly fine (no async and await keywords necessary):

Calculator _calc = new Calculator()
Task<int> CalculateValue()
{
    return calc.Calculate();
}

As I inject most of my dependencies using a DI framework it has been a rare circumstance where I've ran into this problem.

No comments:

Post a Comment