8

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 ?

Raul Agarwal
  • 91
  • 1
  • 4
  • I want to explain but it's already here with simplicity, see if this helps https://stephenhaunts.com/2014/10/10/simple-async-await-example-for-asynchronous-programming/ – PKV Apr 13 '16 at 18:25
  • 1
    A good explanation is [here](http://stackoverflow.com/q/25078668/2530848) also be sure to check stephen's [introductory article](http://blog.stephencleary.com/2012/02/async-and-await.html) – Sriram Sakthivel Apr 13 '16 at 18:49
  • I use Run in order to put synchronous execution in a task (running on task thread pool). i.e. ```var task = Task.Run(() => DoWork()); /** do something else **/ await task;``` – Meirion Hughes Apr 13 '16 at 19:55

3 Answers3

9

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".

Stephen Cleary
  • 437,863
  • 77
  • 675
  • 810
  • Please see the example attached in the updated details. Why calling DEF in fire and forget approach is wrong in this case. I want to continue with ABC (if there is something at all else return from ABC) and in the mean time continue with DEF. But I do not want to mark ABC async / await ? Is it something wrong we are trying to achieve ? – Raul Agarwal Apr 14 '16 at 06:45
  • Let me turn this around. You keep asking for sync-over-async and fire-and-forget, which are both definitely antipatterns. **Why** do you "not want to mark ABC async"? – Stephen Cleary Apr 14 '16 at 12:28
  • Actually in my web api code ..the code is divided in multiple layers. The scenario is something like `First we create a case and we get the case key in return after completion of the DB Call.` This is implemented in sync manner. And now if case key is returned successfully I have some additional inputs regrading case that can be added to DB and that's not must that we wait for them get stored and the result to be returned back. I guess We can fire the `savecase call to DB` and then keep on returning the generated case key all the way up to View. So I did not mark `createcase(here ABC)` async – Raul Agarwal Apr 14 '16 at 12:39
  • @RaulAgarwal: Oh, this is in a WebAPI project? In that case, fire-and-forget is almost certainly what you *don't* want to do. For example, did you know that once you return a result from your controller, ASP.NET can just shut down your AppDomain? Your database saves would be lost. I list [a few workarounds on my blog](http://blog.stephencleary.com/2014/06/fire-and-forget-on-asp-net.html), but the bottom line is that ASP.NET wasn't designed to run code *outside* of a request. – Stephen Cleary Apr 14 '16 at 13:44
  • Oh ok.Thanks for the insight Stephen. So what do you suggest?Async / Await all the way up to controller for CreateCase() also? But that just makes a call to the SaveCaseSearch service layer . So getting confused . Also overhead of waiting the whole amount of same time is still there , right before CreateCase() can return . Any suggestion ? Also one more question : `Task.Run(async()=> await A()).continueAwait(false); ` what does it mean ? Task.Run() runs it on the background thread and ContinueAwait does not require to comeback to current thread? If I do not give await inside it throws warning. – Raul Agarwal Apr 14 '16 at 13:59
  • 2
    You should not use `Task.Run` on ASP.NET. If you use `async` at all, then use it all the way up to the controller. That is the way it should be. If the client wants to do other work while the request is in progress, then the *client* can use asynchronous programming. – Stephen Cleary Apr 14 '16 at 14:15
  • Wow. That helped . What about `Task.Run(async()=> await A()).continueAwait(false);` if `async A()` is the declaration(i.e. it is marked with async/await). What does it mean ? `Task.Run() runs it on the background thread and ContinueAwait does not require to comeback to current thread?` is that correct ? – StrugglingCoder Apr 14 '16 at 14:22
  • `Task.Run` will run `A` on a background thread. `ConfigureAwait(false)` will not have any affect since there is no `await` to configure. If you meant `await Task.Run(async () => await A()).ConfigureAwait(false);`, then the `ConfigureAwait` would cause the containing method to resume on the thread pool context. – Stephen Cleary Apr 14 '16 at 15:08
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."'

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!

  • Please see the example attached in the updated details. Why calling DEF in fire and forget approach is wrong in this case. I want to continue with ABC (if there is something at all else return from ABC) and in the mean time continue with DEF. But I do not want to mark ABC async / await ? Is it something wrong we are trying to achieve. – Raul Agarwal Apr 14 '16 at 06:45
  • also you said "There is no benefit marking DoWork as async in this case, because there is no point having any await statements in DoWork." ... but DEF in turn waits for another long running operation ExecuteStoredProcedureAsync .. so what am I doing wrong ? – Raul Agarwal Apr 14 '16 at 06:47
  • Why can't you just do `Task.Run(() => DEF(SaveCaseSearchInput, AcctLst.ElementAt(0).o_case_seq))` – grumpy_data_engineer Apr 14 '16 at 08:07
  • Why can't you just do `Task.Run(() => DEF(SaveCaseSearchInput, AcctLst.ElementAt(0).o_case_seq))`, if you're absolutely sure you want DEF to run with a fire and forget approach? However your DEF method returns AcctLst which currently has no reference pointing to it in ABC. Are you sure you want it to be a fire and forget Task? – grumpy_data_engineer Apr 14 '16 at 08:14
  • I want to run it background while I want the main ABC() to keep on executing the rest of the method . And when the background task will complete I may or may not want the results back. If I need the result back and also want to run it in background will async/await help or task.run ?How to do that ? Could you please show? I think in first example you asked to use `await Task.Run(()=>LongMethod());` right ? – Raul Agarwal Apr 14 '16 at 09:23
1

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.

  • No I guess actually await must be there in DEF method body right as it is waiting for rep.ExecuteStoredProcedureAsync to complete. Also t. Wait() won't it block the main thread ? Or will it block the background thread on which the task is called ? And what's the difference between await Task.Run() and Task.Run(()=> ......) ? – Raul Agarwal Apr 14 '16 at 11:03
  • If you require the result from the task in ABC, you have to block ABC's thread to wait for the result, there's no choice. I've just shown you how to minimise the blocking. What do you want to happen while waiting for rep.ExecuteStoredProcedure... to complete? You want the caller to keep running right? Well it already is, because the DEF method is running in its own task, so you don't need to write "await". Task.Run always has a parameter. – grumpy_data_engineer Apr 14 '16 at 11:17
  • ...I understand that Sir. But ExecuteStoredProcedureAsync has an async/await call to DB. So it is marked async/await. And hence the caller also . That's why DEF was marked async/await. If I remove async/await from declaration of DEF ..... would not it be all synchronous calls even though DEF is running on background thread and eventually blocking it also? – Raul Agarwal Apr 14 '16 at 11:45