简体   繁体   中英

What is the difference between these awaitable methods?

I'm looking at some asynchronous programming in C# and was wondering what the difference would be between these functions that does the exact same thing and are all awaitable.

public Task<Bar> GetBar(string fooId)
{
    return Task.Run(() =>
    {
        var fooService = new FooService();
        var bar = fooService.GetBar(fooId);
        return bar;
    });
}

public Task<Bar> GetBar(string fooId)
{
    var fooService = new FooService();
    var bar = fooService.GetBar(fooId);
    return Task.FromResult(bar)
}

public async Task<Bar> GetBar(string fooId)
{
    return await Task.Run(() =>
    {
        var fooService = new FooService();
        var bar = fooService.GetBar(fooId);
        return bar;
    });
}

My guess is that the first is the correct way to do things, and the code isn't executed until you try to get the result from the returned Task.

In the second, the code is executed on call and the result is stored in the returned task.

And the third is kind of like the second? The code is executed on call and the result of the Task.Run is return? Which in that case this function would be kind of stupid?

Am I right or am I way off?

None of these method implementations make sense. All you're doing is pushing out blocking work onto the thread pool (or worse, running it synchronously and wrapping the result in a Task<Bar> instance). What you should do instead is expose the synchronous API, and let the callers decide how to call it. Whether they want to use Task.Run or not is then up to them.

Having said that, here are the differences:

#1

The first variant (which returns a Task<Bar> created via Task.Run directly) is the "purest" even if it doesn't make much sense from an API perspective. You're allowing Task.Run to schedule the given work on the thread pool and return the Task<Bar> representing the completion of the async operation to the caller.

#2

The second method (which utilises Task.FromResult ) is not asynchronous. It executes synchronously, just like a regular method call. The result is simply wrapped in a completed Task<Bar> instance.

#3

This is a more convoluted version of the first. You are achieving an outcome similar to what #1 does, but with an extra, unnecessary, and even somewhat dangerous await . This one is worth looking at in more detail.

async/await is great for chaining async operations via combining multiple Task s representing asynchronous work into a single unit ( Task ). It helps you get things happening in the right order, gives you rich control flow between your async operations and ensures that things happen on the right thread.

None of the above, however, is of any benefit in your scenario, because you only have one Task . Therefore, there is no need to make the compiler generate a state machine for you just to accomplish what Task.Run already does.

A poorly designed async method can also be dangerous. By not using ConfigureAwait(false) on your await ed Task you are inadvertently introducing a SynchronizationContext capture, killing performance and introducing a deadlock risk for no benefit.

If your caller decides to block on your Task<Bar> in an environment which has a SynchronizationContext (ie Win Forms, WPF and possibly ASP.NET) via GetBar(fooId).Wait() or GetBar(fooId).Result , they will get a deadlock for the reasons discussed here .

I read somewhere on Stackoverflow in a comment the following analogy. Because it is in a comment I can't find it easily, so no link to it.

Suppose you have to make breakfast. You boil some eggs and toast some bread.

If you start boiling the eggs, then somewhere in the subroutine "Boil Egg" you'll have to wait until the eggs are boiled

Synchronous would be that you wait until the eggs are finished boiling before starting subroutine "Toast Bread".

However it would be more efficient if while the eggs are being boiled, you don't wait, but you start toasting eggs. Then you wait for either of them to finish, and continue the process "Boil Eggs" or "Toast Bread" whichever finishes first. This is asynchronously but not concurrent. It is still one person who is doing everything.

A third method would be to hire a cook who boils eggs while you toast the bread. This is really concurrent: two persons are doing something. If you are really rich, you could also hire a toaster, while you read the newspaper, but hey, we don't all live in Downton Abbey ;-)

Back to your question.

Nr 2: Is synchronous: the main thread does all the work. This thread returns after the egg is boiled before the caller can do anything else.

Nr 1 is not declared async. This means that although you start another thread that will do the work, your caller can't continue doing something else, and although you could, you don't, you just wait until the egg is boiled.

The third procedure is declared async. This means, that as soon as the waiting for the egg starts, your caller can do something else, like toasting bread. Note that this the work all the work is done by one thread.

If your caller would wait not do anything but wait for you it would not be of very much use, unless your caller is also declared async. This would give the caller's caller the opportunity to do something else.

Generally when using async-await properly, you see the following: - Every function that is declared async returns Task instead of void, and Task < TResult > instead of TResult - There is only one exception: the event handler returns void instead of Task. - Every function that calls an async function should be declared async, otherwise using async-await is not really useful - After calling an async method you can start toasting the bread while the egg is being boiled. When the bread is toasted you can await for the egg, or you can check if the egg is ready during the toasting, or maybe most efficient would be to await Task.WhenAny, to continue finishing either the egg or the toast or await Task.WhenAll when you don't have anything useful to do as long as not both are finished.

Hope this analogy helps

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM