CSharpie
CSharpie

Reputation: 9467

When to return task over using async

I am abit confused when it comes to how a method should look like when dealing with async / task.

From my understanding, a method that just creates a new task doesnt need to be async since this would produce overhead as it wraps the hole thing in a new task.

So this:

async Task _doStuff()
{
    await Task.Run(()=> _stuff());
}

Is better this way:

Task _doStuff()
{
    return Task.Run(()=> _stuff());
}

However, it gets a bit more complicated, if there are some preconditionchecks Which way is better then?

async Task _doStuff()
{
    if(stuffWasDone)
        return;
    await Task.Run(()=> _stuff());
}

or

Task _doStuff()
{
    if(stuffWasDone)
        return Task.Run(()=> {}); // this could be a static-readonly in some helper like AsyncHelper.Empty(); as there is no FromResult for Task.

    return Task.Run(()=> _stuff());
}

Upvotes: 1

Views: 254

Answers (1)

Yuval Itzchakov
Yuval Itzchakov

Reputation: 149538

You dont have to use Task.Run in order to generate a Task. A Task is a promise. If a Task doesn't need to execute, don't create one. Using Task.Run has the overhead of invoking an empty lambda on a threadpool thread, you don't need that. Simply returning should suffice.

You may also use Task.FromResult if no Task is actually needed:

Task DoStuffAsync()
{
    if(stuffWasDone)
    {
       return Task.FromResult(0);
    }

    return Task.Run(() => _stuff());
}

If you don't want to re-generate the Task over again, you can simply put it in a local variable:

private static Task _emptyTask = Task.FromResult(0);

As for your first example of await vs return await, you cannot say one is "better" then the other, as they serve different purposes. The former will asynchronously wait for the Task to complete before returning while the latter will return a hot task to the caller. This will matter in regards of exception handling. More so, if you're not going to be consuming the result of the task afterwards, using await will incur in a redundant generation of a state machine.

As another side note, wrapping synchronous methods in asynchronous wrappers is bad practice. Let the end user explicitly call Task.Run instead, don't fool them with async behavior.

Upvotes: 4

Related Questions