What is the difference between these expected methods?

I look at some kind of asynchronous C # programming and wonder what is the difference between these functions, which does the same thing and they are all waiting.

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; }); } 

I assume that the first is the right way to do something, and the code does not execute until you try to get the result from the returned task.

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

And the third kind of like the second? The code is executed on a call and the result of Task.Run is returned? What in this case will this function look silly?

Am I right or am I away?

+5
source share
2 answers

None of these implementations of the method make sense. All you do is push the lockout to the thread pool (or, worse, run it synchronously and wrap the result with an instance of Task<Bar> ). Instead, you should open the synchronous API and let the callers decide how to call it. Whether they want to use Task.Run or not, it depends on them.

Having said that, here are the differences:

# 1

The first option (which returns a Task<Bar> created directly with Task.Run ) is the “cleanest” one, even if it doesn't make much sense from an API point of view. You authorize Task.Run plan this work in the thread pool and return Task<Bar> , represents the completion of an operation calling async.

# 2

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

# 3

This is a more confusing version of the first. You achieve a result similar to what No. 1 does, but with an additional, unnecessary and even somewhat dangerous await . It is worth a look in more detail.

async/await great for a chain of asynchronous operations by combining several Task , representing asynchronous work in one block ( Task ). This helps you achieve something in the correct order, gives you a rich flow of control between your asynchronous operations, and ensures that everything happens in the correct thread.

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

An incorrectly designed async method can also be dangerous. By not using ConfigureAwait(false) on await ed Task , you inadvertently introduce a SynchronizationContext capture, killing performance and introducing the risk of deadlock without any benefits.

If your caller decides to lock your Task<Bar> in an environment with a SynchronizationContext (i.e. Win Forms, WPF, and possibly ASP.NET) via GetBar(fooId).Wait() or GetBar(fooId).Result , these are the reasons discussed here .

+6
source

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

Suppose you have to have breakfast. You cook some eggs and smoke bread.

If you start to boil eggs, then somewhere in the Boil Egg subroutine you will have to wait until the eggs are boiled.

It would be synchronous that you wait until the eggs have finished boiling before starting the Toast Bread routine.

However, it would be more effective if, while the eggs are boiling, you do not wait, but begin to fry the eggs. Then you wait until one of them finishes, and continue the “Boiled Eggs” or “Toast Bread” process, which ends first. It is asynchronous, but not parallel. This is still one person who does everything.

The third way is to hire a cook who boils eggs while you are baking bread. This is really simultaneous: two people are doing something. If you are really rich, you can 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: synchronously: the main thread does all the work. This flow returns after the egg boils before the caller can do anything else.

Nr 1 is not declared async. This means that although you are launching another thread that will do this work, your caller cannot continue to do anything else, and although you can do this, you simply cannot wait for the egg to boil.

The third procedure is declared async. This means that as soon as the waiting for the egg begins, your caller can do something else, such as toasting bread. Please note that this work is completely performed by a single thread.

If your caller waits to do nothing but wait for you, it will not be very useful if your caller is also not declared async. This would give the caller the opportunity to do something else.

As a rule, when using async-wait correctly, you will see the following: - Each function 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. - Each function that calls the async function must be declared as aync, otherwise using the async wait is not very useful. - After calling the asynchronous method, you can start toasting the bread while the egg is boiling. When the bread is toasted, you can wait for the egg, or you can check if the egg is ready during the toast, or maybe it would be most effective to wait for Task.WhenAny to continue the finishing egg or toast or wait for Task. WhenAll when you have nothing useful, if not both completed.

Hope this analogy helps

0
source

Source: https://habr.com/ru/post/1243565/


All Articles