Async/await vs Task.Run in C#

假装没事ソ 提交于 2021-02-18 12:16:25

问题


I am just new to this world of asynchronous stuff.
Please bear with my lack of knowledge.

  1. It is said when a method encounters await ... "It tells the awaitable to run the remainder of the method when it completes, and then returns from the async method."
    I did not get this part.
    So does it mean the method still keeps on running synchronously and waits till the awaitable returns and then proceeds with the rest of the method?
    If not please explain then why Task.Run is needed to run a method in the background or in a fire and forget manner. I could still be achieved via await also right? i.e.
    The method keeps on executing the rest of the statements without waiting for the await to return.
    I hope that is similar to a background run approach. Or is not it? I am confused.

  2. If a method is marked with async and await and that in turn calls another method asynchronously in a separate layer that's also marked with async and await ..
    then how the the call of the first method that's marked with async and await from a separate method say name ABC should look like?
    I do not want to annotate that method to be async/await. So

    Task.Run(() => DoWork());
    

    from ABC () is fine without marking it async/await?
    Or is it against the principle of asynchrony?

Here is what I am trying to achieve ...

   public IList<CreateCaseOutput> ABC(CreateCaseInput CreateCaseInput,SaveCaseSearchInput SaveCaseSearchInput)
    {
        CaseSQL.getABCParameters(CreateCaseInput, RequestType, out strSPQuery, out listParam);
        var AcctLst = rep.ExecuteStoredProcedure<CreateCaseOutput>(strSPQuery, listParam).ToList();

        if (!string.IsNullOrEmpty(AcctLst.ElementAt(0).o_case_seq.ToString()))
        {
            Task.Run(async () =>
            {
                await DEF(SaveCaseSearchInput, AcctLst.ElementAt(0).o_case_seq);
            }).ConfigureAwait(false);              
        }
        console.writeLine("After Async called");
        return AcctLst;
    }

    public async Task<SaveCaseSearchOutput>> DEF(SaveCaseSearchInput SaveCaseSearchInput,Int64? case_key)
    {

            CaseSQL.getDEFParameters(SaveCaseSearchInput, case_key, out strSPQuery, out listParam);
            var AcctLst = await rep.ExecuteStoredProcedureAsync<SaveCaseSearchOutput>(strSPQuery, listParam);
            return AcctLst;
    }

DEF which is async/await needs to be called in the background in fire and forget approach from ABC and once fired I want to continue with rest of ABC and run DEF in the background. What's wrong in this fire and forget approach ? If I call

only

DEF(SaveCaseSearchInput, AcctLst.ElementAt(0).o_case_seq); 

instead of

 Task.Run(async () =>
                {
                    await DEF(SaveCaseSearchInput, AcctLst.ElementAt(0).o_case_seq);
                }).ConfigureAwait(false);              
            }
            return AcctLst;

then this code runs synchronously in debugger.

Am I doing something wrong ?


回答1:


So does it mean the method still keeps on running synchronously and waits till the awaitable returns and then proceeds with the rest of the method?

No. The awaitable has a "callback" mechanism. So, the method just registers itself with the awaitable. When the awaitable completes, it will execute its callbacks, which include the continuation of the async method.

In the meantime, the async method returns an incomplete task.

If not please explain then why Task.Run is needed to run a method in the background or in a fire and forget manner.

If you want to run on a background thread, then use Task.Run. Task.Run just schedules work to the thread pool.

I could still be achieved via await also right? i.e. The method keeps on executing the rest of the statements without waiting for the await to return. I hope that is similar to a background run approach. Or is not it? I am confused.

Well, you could "forget" the task by just not awaiting it:

MyMethod()
{
  SomeMethodAsync(); // Note: no await; we just ignore the task.
}

You almost never want to do "fire and forget", though. Easily >90% of the time people ask for it, it's actually a design error.

I do not want to annotate that method to be async/await... Or is it against the principle of asynchrony?

That's against the principle. If you think about it, it doesn't really make sense to block on asynchronous code. You want to go through the trouble of making a method asynchronous (meaning it doesn't block a thread), and then block a thread on it? Why, exactly? Sometimes in practice, the code will temporarily end up in this kind of state during a transition to asynchronous code, but it's a tricky place to be.

For more information, see my article on async best practices, particularly "async all the way".




回答2:


'It is said when a method encounters await ... "It tells the awaitable to run the remainder of the method when it completes, and then returns from the async method."'

So let's say you have a method A that calls a method B. Method B has the "async" keyword in its declaration. In method B there is a time-consuming call to LongMethod, which ultimately returns a string.

What we want here is for method A to not have to wait for ages for method B's LongMethod to complete. So we write in Method B:

string newVariable = await Task.Run(()=>LongMethod());

If LongMethod itself is also async, then LongMethod can only return a void, a non generic Task or a generic Task. Therefore we would have to change the return type in the LongMethod declaration to Task<string>

So back to our story, the LongMethod task has started... At this point method A resumes from the next line of code after the call to method B, and method B continues to wait for the LongMethod to finish, so there are now two threads running. Eventually, the LongMethod completes and method B runs to the end, meanwhile method A has continued to run, or has maybe even finished running.

I hope this gives you a picture of the relationship between Async/Await and Tasks.

Now for question 2:

I think your question is basically asking if Method A in my example needs to be marked "async" because it makes a call to a method marked "async", and no it does not. Also please note that ABC() just needs to have

DoWork() Not Task.Run(() => DoWork());

ABC will then stop running until the DoWork method arrives at an 'await' statement.

If DoWork just needs to be run as its own task, and you don't want ABC() to stop at all, then just do

Task.Run(() => DoWork());

There is no benefit marking DoWork as async in this case, because there is no point having any await statements in DoWork.

I hope this helps, sorry if I've misinterpreted your questions!




回答3:


From what you said in your comment (I'll assume you need the result in ABC)

You just need to write

Task<SaveCaseSearchOutput> t = Task.Run(() =>DEF(SaveCaseSearchInput,AcctLst.ElementAt(0).o_case_seq));

in ABC, instead of the current Task.Run call.

Then at the line in ABC when you require the result write

t.Wait();
SaveCaseSearchOutput g = t.Result;

DEF must remain async but you can remove the await statement in DEF.



来源:https://stackoverflow.com/questions/36606519/async-await-vs-task-run-in-c-sharp

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!