Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

To Task.Run or not to Task.Run

Suppose I have an interface which includes an async method, and I have two different implementations of that interface. One of the two implementations is naturally async, and the other is not. What would be the "most correct" way of implementing the non-async method?

public interface ISomething {
    Task<Foo> DoSomethingAsync();
}

// Normal async implementation
public class Implementation1 : ISomething {
    async Task<Foo> ISomething.DoSomethingAsync() {
        return await DoSomethingElseAsync();
    }
}

// Non-async implementation
public class Implementation2 : ISomething {
    // Should it be:
    async Task<Foo> ISomething.DoSomethingAsync() {
        return await Task.Run(() => DoSomethingElse());
    }
    // Or:
    async Task<Foo> ISomething.DoSomethingAsync() {
        return DoSomethingElse();
    }
}

I try to keep up with Stephen Cleary's blog, and I know neither one of these actually provides any async benefits, and I'm ok with that. The second one seems more correct to me, since it doesn't pretend to be something it's not, but it does give a compiler warning, and those add up and get distracting.

This would all be inside ASP.NET (both web MVC and WebAPI), if that makes a difference.

like image 705
Joe Enos Avatar asked Mar 08 '15 17:03

Joe Enos


1 Answers

You can forgo the async modifier altogether and use Task.FromResult to return a completed task synchronously:

Task<Foo> ISomething.DoSomethingAsync()
{
    return Task.FromResult(DoSomethingElse());
}

This takes care of the warning and has better performance as it doesn't need the state machine overhead of an async method.

However, this does change the semantics of exception handling a bit. If that's an issue then you should use the synchronous async method approach and accept the warning (or turn it off with a comment):

#pragma warning disable 1998
    async Task<Foo> ISomething.DoSomethingAsync() 
#pragma warning restore 1998
    {
        return DoSomethingElse();
    }

As Stephen Cleary suggested you can also take care of that warning (while keeping the method synchronous) by awaiting an already completed task:

async Task<Foo> ISomething.DoSomethingAsync() 
{
    await Task.FromResult(false); // or Task.CompletedTask in .Net 4.6
    return DoSomethingElse();
}
like image 192
i3arnon Avatar answered Oct 22 '22 02:10

i3arnon