I have this line of code:
t.ContinueWith(_ => form.Close(),
TaskScheduler.FromCurrentSynchronizationContext());
...about which the compiler has this to say:
Warning 2 Because this call is not awaited, execution of the current method continues before the call is completed. Consider applying the 'await' operator to the result of the call.
Now, this wasn't code that I wrote, but I thought that it's simply adding a continuation to the end of an existing task. I didn't think it's actually running the task (or the continuation). So surely this process of merely modifying the task is a synchronous operation? Why would I have to await it?
Why would I have to await it?
You don't have to await it, that's why it is a warning, not an error. But, if you're in an async method and you have a method that returns an awaitable object, most of the time, you shouldn't ignore it.
In normal synchronous code, you don't have to do anything special to wait until a method you called completes, each method call always blocks. But with asynchronous methods, you actually have to await them if you want to wait until they complete. Also, if the asynchronous operation fails and throws an exception, you won't know about it until you await the result of the method (or get the exception from the result some other way, such as calling Wait() if the awaitable is a Task).
So, if you ignore a returned awaitable value in an async method, it's likely that you have a bug in your code, which is what the warning is trying to avoid.
In your case, ContinueWith() returns a Task, which can be awaited, so the compiler assumes that you should await it. But in this case, you don't want to wait until the continuation completes and Close() most likely won't throw an exception. So, in this case, the warning is a false positive, it does not actually indicate a problem in the code.
Related
This question already has answers here:
When does 'await' immediately leave a method and when does it not?
(6 answers)
Closed 9 months ago.
After reading a lot about async/await I still have a fundamental question (that I kind of assume the answer but I just want confirmation).
Let's discuss over some code:
async Task methodA() {
//code A (without awaits or Task.Run)
Task.Run(()=> //code A' (without awaits or Task.Run))
}
async Task methodB() {
//code B (without awaits or Task.Run)
await methodA();
}
void Main() {
//code C (without awaits or Task.Run)
methodB(); (no await)
//code D (without awaits or Task.Run, no pun intended)
}
What I think will happen:
Run code C
Run code B
Run code A
Run code A' and code D (different threads, in parallel)
The doubt comes from what's in the documentation:
The await operator suspends evaluation of the enclosing async method
until the asynchronous operation represented by its operand completes. [...]
The await operator doesn't block the thread that evaluates the async method.
The key being here what represents an asynchronous operation. Cause considering this other piece of documentation:
The await operator tells the compiler that the async method can't
continue past that point until the awaited asynchronous process is
complete. In the meantime, control returns to the caller of the async
method.
One might think that, since methodA() is async, once methodB reaches the await methodA(), then it might return control to the caller and allow to run code D in parallel to code A and code A'.
The essence is if method A constitutes an asynchronous operation as described in the documentation, or only until it reaches truly async code (code that runs on another thread) then it actually allows control to return to the caller (which in this case is code A').
My understanding is that asynchronous operation is an operation that will run on another thread, is this correct? So things like Task.Run, Task.Delay, Task.Sleep..? And that even though code A is within an async method, it is not an async operation and control does not go immediately back when awaiting methodA. Is this correct?
Just for completion, check this question where in the answer's comments they argue precisely about this, without a very clear answer.
[...], control returns to the caller of the async method.
This sentence had confused the hell out of me when I was learning asynchronous programming. What it means is that the Task is created and returned. You can think the asynchronous methods as generators for Task objects. The async method has to do some work in order to create the Task. This work completes when the first await of an incomplete awaitable inside the method is reached. At that point the Task is created, it is handed to the caller of the method, and the caller can do whatever it wants with it.
Usually the caller awaits the task, but it can also do other things before awaiting it, or it might never await it and completely ignore it, although this is not recommended. Generally you want to keep track of your tasks, and not let them run unattended in a fire-and-forget fashion.
When the caller does not await immediately the returned task, and the task was not in a completed state upon creation, you introduce concurrency in your application: More than one things might be happening at overlapping time spans. You can read about the differences between concurrency and parallelism here.
I run these two versions and it seems like each is giving me the same output, which is sequential.
ContinueWith_Method1a();
ContinueWith_Method1b();
and
Task.Run(() => ContinueWith_Method1a())
.ContinueWith(task => ContinueWith_Method1b())
.Wait();
I tried with the below but get this error:
Cannot implicitly convert type 'void' to 'System.Threading.Tasks.Task'
Task t = Task.Run(() => ContinueWith_Method1a())
.ContinueWith(task => ContinueWith_Method1b())
.Wait();
static void ContinueWith_Method1a()
{
System.Console.WriteLine("Continue With Method 1a started.");
Thread.Sleep(1000);// Simulate task processing time.
System.Console.WriteLine("Continue With Method 1a completed.");
}
static void ContinueWith_Method1b()
{
System.Console.WriteLine("Continue With Method 1b started.");
Thread.Sleep(1000);// Simulate task processing time.
System.Console.WriteLine("Continue With Method 1b completed.");
}
ContinueWith starts the next Task after the first finished.
So both your examples run synchroniously ( first methode A then methode b)
EDIT:
As an addition to your edit, your error is caused by the .Wait() function, which awaits the Task and returns void.
What's the difference with ContinueWith and without?
The title question of your post is very misleading. The use of ContinueWith() in your code examples is the least interesting difference. The much more significant difference is that the first example just calls the methods directly, while the second uses Task.Run().
Now, as it happens there would be no reason to write the code as in your second example. Even ignoring the ContinueWith() aspect, if all your task does is call a method, and all you're going to do with the task is synchronously wait on the task (i.e. call the Wait() method), then you might as well just call that method in your current thread. No need for Task.Run() in that situation.
Similarly, if all you're going to do with ContinueWith() is call another method immediately after, you might as well just call the method directly.
By blocking in the current thread, you've rendered useless any advantage there might have been in executing those two methods asynchronously.
Of course, there is a clear mechanical difference. You get the same results, ultimately, but how that result is achieved is very different. In the first example, you just call the methods, one after another. Each call executes in the current thread. In the second example, you are essentially asking the thread pool to execute each method in sequence, while you block the current thread waiting for that to happen.
It's similar to the difference between you peeling an onion and then chopping it yourself, and asking someone else to peel an onion and then chop it while you sit around twiddling your thumbs waiting for them to finish those actions.
Finally, as far as the error message you got, "Cannot implicitly convert type 'void' to 'System.Threading.Tasks.Task'", that only happened because you added the Task t variable and tried to set it to the result of the Wait() method. Since the Wait() method doesn't return anything (i.e. has a declared return type of void), never mind a value of Task, it's not possible to set the value of t using its return value. Hence the error message. If you want to assign a Task variable, you would need to just leave out the call to Wait():
Task t = Task.Run(() => ContinueWith_Method1a())
.ContinueWith(task => ContinueWith_Method1b());
I'm currently getting into the async/await keywords, and went through the following questions: When correctly use Task.Run and when just async-await and Async/Await vs Threads
However even the second link doesn't answer my question, which is when to simply use
Task.Run(...)
versus
await Task.Run(...)
Is it situational or is there something to be gained by using await (and thus returning to the caller)?
The code Task.Run(...) (in both examples) sends a delegate to the thread pool and returns a task that will contain the results of that delegate. These "results" can be an actual return value, or it can be an exception. You can use the returned task to detect when the results are available (i.e., when the delegate has completed).
So, you should make use of the Task if any of the following are true:
You need to detect when the operation completed and respond in some way.
You need to know if the operation completed successfully.
You need to handle exceptions from the operation.
You need to do something with the return value of the operation.
If your calling code needs to do any of those, then use await:
await Task.Run(...);
With long-running background tasks, sometimes you want to do those (e.g., detect exceptions), but you don't want to do it right away; in this case, just save the Task and then await it at some other point in your application:
this.myBackgroundTask = Task.Run(...);
If you don't need any of the above, then you can do a "fire and forget", as such:
var _ = Task.Run(...); // or just "Task.Run(...);"
Note that true "fire and forget" applications are extremely rare. It's literally saying "run this code, but I don't care whether it completes, when it completes, or whether it was successful".
You can use Task.Run() when handling logic with fire and forget type, similar to invoking events if someone is subscribed to them. You can use this for logging, notifying, etc.
If you depend of the result or actions executed in your method, you need to use await Task.Run() as it pauses current execution until your task is finished.
I am just new to this world of asynchronous stuff.
Please bear with my lack of knowledge.
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.
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 ?
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".
'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!
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.
Consider an asynchronous (*Async / async) function that is called twice, one time with await and other without it.
If I use await, will it wait until the asynchronous function is executed and then execute the next line?
await db.SaveChangesAsync();
//Some code here
And, if I don't use await, will it continue with executing the code below it without waiting for the asynchronous function to complete?
db.SaveChangesAsync();
//Some code here
Am, I right over here or await is mandatory when it comes to async functions?
If I use await, will it wait till the async function is executed and then execute the next line.
That depends on the method returns. It returns a Task[<T>], which represents a future value. If the Task is already completed, it keeps running synchronously. Otherwise, the remaining portion of the current method is added as a continuation - a delegate callback that is invoked when the asynchronous portion reports completion.
What it does not do, in either case, is block the calling thread while the asynchronous part continues.
And, if I don't use await, will it continue with executing the code below it without waiting for the async function to complete.
Yes
...or await is mandatory when it comes to async functions?
When invoking them? Not mandatory, but it does make things convenient. Usually, such functions return a Task[<T>], which can also be consumed via different code. You could use .ContinueWith(...), .Wait, .Result, etc. Or you could just ignore the Task completely. The last is a bad idea, because exceptions on asynchronous functions need to go somewhere: very bad things happen if exceptions are not observed.
When writing them, however: if you write an async method and don't have an await keyword in there, the compiler will present a warning indicating that you're probably doing something wrong. Again, not strictly mandatory, but highly recommended.