3

This is something I've come across while refactoring some legacy code.

Consider a method on an interface that returns a Task:

public interface IFoo
{
    Task Bar();
}

The Bar method implementation can be implemented in two ways:

Returning a Task:

public class Foo1 : IFoo
{
    public Task Bar()
    {
        return Task.Run(() =>
            {
                /* some work */
            });
    }
}

Or using async ... await:

public class Foo2 : IFoo
{
    public async Task Bar()
    {
        await Task.Run(() =>
            {
                /* some work */
            });
    }
}

Are these implementations functionally equivalent, or are there (potentially subtle) differences?

Richard Ev
  • 52,939
  • 59
  • 191
  • 278
  • 1
    From my knowledge the difference is not as you presented here. When you implement Foo1, you will need to call Task.Wait() and for Foo2 just await Task, so there is the difference. Explained here: http://stackoverflow.com/questions/9519414/whats-the-difference-between-task-start-wait-and-async-await – Razvan Dumitru Jun 02 '16 at 08:39
  • @RazvanDumitru why call `Task.Wait()` at all? As far as callers are concerned the two methods are the same. `await foo1Instance.Bar();` and `await foo2Instance.Bar();` would work in the same way. There's no reason to block the first one just to return a completed task. . Did you mean something else perhaps? – Panagiotis Kanavos Jun 02 '16 at 13:33
  • Yea. U're correct. There is no need to wait at all. You can await both of returned objects. – Razvan Dumitru Jun 02 '16 at 13:55

3 Answers3

2

There is quite a difference since using the async-await syntax causes the compiler to generate code which actually continues past the await statement once the task finishes. Moreover, awaiting a task which has faulted results in the exception bubbling to the awaiter, meaning that it is no longer considered unobserved.. I think that there's more, and I really recommend you have a look at Jon Skeet's C# 5 Async course on pluralsight, he actually goes through compiler generated code and explains it.

Eyal Perry
  • 2,255
  • 18
  • 26
1

Returning a Task

Foo1.Bar is just a regular synchronous method, which returns some object instance (Task in particular).

Or using async ... await

Foo2.Bar is asynchronous method, which will be compiled into state machine. Some overhead will take place here. Note, that future version of Roslyn will convert methods like this into synchronous ones.

But, actually, you shouldn't use Task.Run here. See this great post for details.

Dennis
  • 37,026
  • 10
  • 82
  • 150
  • That's not what the article says. In the end it *does* use Task.Run. The advice is against using Task.Run in a method's implementation to automagically convert a CPU-bound method into an "asynchronous" one – Panagiotis Kanavos Jun 02 '16 at 13:36
  • Also, *both* methods are asynchronous. Both perform their work using a separate Task. The second one though, actually *awaits* some *other* asynchronous call. – Panagiotis Kanavos Jun 02 '16 at 13:38
  • @PanagiotisKanavos: disagree. 1st one *is* synchronous. It just starts some task, but there's nothing asynchronous in the method itself - starting task or thread from method doesn't make it asynchronous. Also, you're misunderstanding linked post. It's about allowing the caller to decide, whether to use `Task.Run` or not, when implementation isn't "true" asynchronous code. – Dennis Jun 02 '16 at 13:42
0

well, the second implementation looks unnecessary complicated. if you just need to return a task, create a task and return it like the first implementation.

the second implementation returns a task which encapsulate yet another awaited task. this will yield unwanted code bloat, memory usage and possibly another thread creation, just to achieve the same goal as the first.

David Haim
  • 25,446
  • 3
  • 44
  • 78
  • The second code snippet enables execution of more code once the task completes, provided that an exception is not thrown.. anyways, goals were not discussed- the question revolves around the difference between the two. – Eyal Perry Jun 02 '16 at 09:39
  • @EyalPerry that's incorrect. You can use `ContinueWith` to run code after a task completes. That's how Tasks were used in .NET 4.0 and *still* used when the state machine generated by `async/await` is undesirable – Panagiotis Kanavos Jun 02 '16 at 13:39
  • What you said is true, but does not negate what I said.. @PanagiotisKanavos – Eyal Perry Jun 02 '16 at 13:41