Would these calls behave similarly? As in would they behave the same way async-await runs - can or cannot run on the same original thread context? Or would the first one ensure that the execution happens in a different thread context?
First way -
Task task = SomeAsyncTask();
await task();
Second way -
await SomeAsyncTask();
If there is code between Task task = SomeAsyncTask(); and await task();, your task will start executing async code and your current thread will execute that code at the same time.
Otherwise, they are equal..
Task task = SomeAsyncTask();
//Codes executed on current thread while SomeAsyncTask is running
await task();
All asynchronous methods start synchronously.
That is:
await SomeAsyncTask();
is exactly the same as:
var task = SomeAsyncTask();
await task;
In both cases, SomeAsyncTask will run until it is either complete or hits an asynchronous await, at which point it returns a task. That task is then awaited by the calling method.
Related
TLDR: In my example (ASP.NET) below why does task1.Result and task2.Result result in a deadlock and task3.Result does not?
We have a class in our code, where I cannot easily mark the method as async and keep the code from blocking while waiting for an async task to finish. The reason is that our framework does not support that. Therefore I tried to find a solution with task.Result and got some deadlocks. Luckily I found a solution (see task3). Now I tried to find out, what is the difference between task1, task2 and task3 to understand why the first two result in a deadlock and the third does not.
Using the debugger I did not see any difference like task3 being run before task3.Result is called. It is still in WaitingForActivation state like the other two. Can anyone explain to me, how task3 can work?
public class HomeController : Controller
{
public ActionResult GetSomething()
{
var task1 = GetSomethingAsync();
var task2 = Task.Run(async () => await task1);
var task3 = Task.Run(async () => await GetSomethingAsync());
return Content(task1.Result);
// return Content(task2.Result);
// return Content(task3.Result);
}
private static async Task<string> GetSomethingAsync()
{
return await Task.Run(() => "something");
}
}
The root of the problem is the await here:
private static async Task<string> GetSomethingAsync()
{
return await Task.Run(() => "something");
}
The context is captured before entering the awaiting, and the context is the main thread. So after the awaiting the context must be restored, so the continuation is scheduled to run in the main thread. Since the main thread is blocked at this point, it can't process the continuation, hence the deadlock.
To prevent the capturing of the context you could configure this await with ConfigureAwait(false).
Update: By continuation I mean the code inside GetSomethingAsync that follows after the awaiting. Although there is no code after that, it seems that the compiler does bother to create an actual continuation for this no-op part of the method (otherwise a deadlock should not occur in your example).
It should be noted that the compiler transforms an async method to a Task that consists of multiple mini-tasks. Every await encountered in the path of the execution causes the creation of a mini-task that is the continuation of the awaited task. All these mini-tasks are completed one after the other, and the completion of the last one signals the completion of the "master-task" that is returned by the async method.
What ever GetSomethingAsync() does it is done by the calling thread until some operation (OP) can not be completed right away (for example io) then the controlflow is given to the calling function but.
If you then access the Result property on the returned Task object the thread will be blocked.
This leads to the problem that even is the OP is finished the thread will not know about it because he is busy waiting for the completion of the Task
If however you let GetSomethingAsync() be exectuted by some thread-pool thread (which Task.Run(...) does) the thread-pool thread can finish the OP and the calling thread can be notified that the Task has completed.
Update:
Your second approch does not work because the task was still started on your main thread. If you have this methode
public static async Task DoStuffAsync()
{
Console.WriteLine($"Doing some stuff1 on thread {Thread.CurrentThread.ManagedThreadId}");
await Task.Delay(50);
Console.WriteLine($"Doing some stuff2 on thread {Thread.CurrentThread.ManagedThreadId}");
}
and run this code in an appilcation with an SynchronizationContext
var task = DoStuffAsync();
Console.WriteLine($"Doing main stuff on thread {Thread.CurrentThread.ManagedThreadId}");
await Task.Run(async () => await task);
It will output something like:
Doing some stuff1 on thread 1
Doing main stuff on thread 1
Doing some stuff2 on thread 1
So with the code line Task.Run(async () => await task) you only achieved that a thread-pool thread waits on the completion of your original Task, but this in turn creates a new Task that if not handeld by awaiting it causes a deadlock.
There are following methods,
async Task DoWork1Async() { .... };
async Task DoWork2Async() { .... };
async Task DoWork3Async() { .... };
I read the following code
await Task.Run(() => DoWork1Async());
await Task.Run(() => DoWork2Async());
await Task.Run(() => DoWork3Async());
instead of
await DoWork1Async();
await DoWork2Async();
await DoWork3Async();
What's the difference between these two?
When you run (within a method marked async)
await DoWork1Async();
your code calls DoWork1Async within a state machine the compiler sets up. At that point, you code relinquishes control back to that state machine. When the task completes, the rest of the code in your method continues.
Remember that async code doesn't necessarily run on a separate thread (for example, if you are doing asynchronous I/O).
When you run:
await Task.Run(() => DoWork1Async());
your DoWork1Async is dispatched as work to the Thread Pool. It is executed on a different thread (a thread pool thread). When that work is completed, that same state machine mechanism hands control back to you code to continue running.
In the second case, your code always runs on a thread pool thread. In the first case, you may not be using an extra thread at all (depending on how DoWork1Async is coded)
await Task.Run(() => DoWork1Async());
Starts a Task, which will call DoWork1Async. This is guaranteed to be asynchronous no matter how DoWork1Async is implemented.
Note: Task.Run(Action<Task<T>>) returns a Task<T>,
not a Task<Task<T>>. When your lambda returns a Task, Task.Run() returns a Task that completes with the result of the inner task. Thus you do not need to do awkward things like await await Task.Run(() => return a Task).
await DoWork1Async();
Calls DoWork1Async synchronously on the current thread. If DoWork1Async executes an await statement, then work will be suspended and the remainder of the work will occur asynchronously. However, if DoWork1Async completes execution without hitting an await, then control will return to your caller and will then start DoWork2Async all synchronously.
So in short, the first form guarantees the DoWork1Async won't start or finish synchronously.
The second form will start DoWork1Async synchronously, and may even finish it synchronously depending upon how it is written.
--
Here's a fiddle showing the difference:
https://dotnetfiddle.net/GhrO8x
Notice how in the first case DoWork() starts and executes completely synchronously before we even await its Task while in the 2nd case it executes asynchronously after we await its task.
I may get voted as a duplicate of
wrapping-synchronous-code-into-asynchronous-call
However, My question is what if I don't care about the results of the task?
More detail: I want the task to run and whether or not it finishes, I want the task to run again in another thread.
To explain specifically, I have a service that reads a table and gets all the records that have not been processed. The task does it's business, but I don't know if there is one record or 1000 records to be processed in that instance. If it is 1000, it will take several minutes, one will take less than a second, usually the task finds nothing to do. So in that case I don't need to know the results of the previous task nor wait for it to finish.
I have read on async and if I do not consume the await result, I read that it will not continue. Is that true?
The example is:
private async Task MakeRequest()
{
mainTimer.Stop();
var task = Task.Run(() => Exec());
await task;
mainTimer.Start();
}
does this mean that the task is not ready to run again unless I have the "await task" command?
await task;
States that you want to wait for task completion and continue execute next lines.
When you run this:
Task.Run(() => Exec());
It start to execute new task immediately, doesn't matter did you use await next or not. You can run the same function in a task as many times as you want without using keyword await. But you need make sure that your function can handle concurrent execution.
If you need to get result of your task without using await, you can continue execution on the same thread on which task was started:
task.ContinueWith(task=> {});
if you don't care about the result, then use a void function:
private async void MakeRequest()
{
mainTimer.Stop();
var task = Task.Run(() => Exec());
await task;
mainTimer.Start();
}
Also, not consuming a task does not block anything, it will finish even if you don't await it.
This example partly is taken from stack
public async Task MyMethod(string s1,sring s2)
{
Trace.TraceInformation("info");
//check input
if (s1 == null || s2 == null)
Trace.TraceInformation("error");
Task<int> longRunningTask = LongRunningOperation();
//indeed you can do independent to the int result work here
//and now we call await on the task
int result = await longRunningTask;
//use the result
Console.WriteLine(result);
}
public async Task<int> LongRunningOperation() // assume we return an int from this long running operation
{
await Task.Delay(1000); //1 seconds delay
return 1;
}
I want the MyMethod to be not blocking.
1.so is it correct that until Task<int> longRunningTask = LongRunningOperation();
the user is blocked?Should I runn the above part of code (the trace and input check) asynchronously too?if yes how? is there a way to do that without new async method implementation?
2.When we start:
Task<int> longRunningTask = LongRunningOperation();
starts executing LongRunningOperation
independent work is done on let's assume the Main Thread (Thread ID = 1) then await longRunningOperation is reached.
Now, if the longRunningOperation hasn't finished and is still running MyMethod() will return to its calling method, thus the main thread doesn't get blocked. When the longRunningOperation is done then a thread from the ThreadPool (can be any thread) will return to MyMethod() at its previous state and continue execution (in this case printing the result to the console).
A second case would be that the longRunningOperation has already finished its execution and the result is available. When reaching the await longRunningOperation the compiler knows that it has the result and will keep on executing code on the very same thread. (in this case printing result to console).
3.What happens if exception is thrown during async method?
4.If the synchronous implementation is provided can I use it?how?
1. You are incorrect about where the code retuns to the user.
Everything up to int result = await longRunningTask; is run on the same thread as the function that called MyMethod(, not the Task<int> longRunningTask = LongRunningOperation(); call.
If you want the synchronous portion of the function to also run in a background thread wrap the call to MyMethod in a Task.Run(
var myMethodTask = Task.Run(() => MyMethod("foo", "bar"));
you then can await or whatever you need on that returned task.
2. If longRunningTask is complete by the time you reach await longRunningTask the function will still continue to run on the same thread and never return to the caller till it reaches the end of the function. When it reaches the end of the function it returns to the caller with a Task already in the completed state.
If longRunningTask was not done it will return to the caller at the point of the await and return a Task that will change from the Running state to the Completed state when the function completes. When the await returns it uses the SynchronizationContext that was active when the task started to figure out what thread it should continue it's work on. If you where on the UI thread when you hit the await the work will continue on the UI thread, by default it will use a ThreadPool thread to continue the work if there was no SynchronizationContext set.
3. If a exception is thrown you see the exception when await longRunningTask returns (or the parent thread returns if you wrapped the function in a Task.Run()
4. You should use a separate method for the synchronous implementation. See the two Microsoft blog posts "Should I expose asynchronous wrappers for synchronous methods?" and "Should I expose synchronous wrappers for asynchronous methods?"
In his answer Stephen explained that when ConfigureAwait(false) is called the rest of the method will be executed on a thread pool thread unless the Task you're awaiting is already complete.
What is clear: If I use ConfigureAwait(false) everything executed after the asynchronous call will be executed on a thread pool thread and therefore not run in the UI SynchronizationContext, otherwise (escpecially needed for UI based things like textBox1.Text = data.Property) it runs within UI SynchronizationContext.
What I don't understand is: Does await not mean that the Task I am waiting for is always completed before the methods is going on? So how can the Task not be completed before going on?
It's important to understand the precendence order. In particular, the dot operator (.) is of higher priority than await. So, when you do:
await SomeMethodAsync().ConfigureAwait(false);
That's identical to:
var task = SomeMethodAsync();
var awaitable = task.ConfigureAwait(false);
await awaitable;
So, the ConfigureAwait is evaluated before the await expression, which actually does the (asynchronous) waiting.
Imagine something like this
var task = obj.SomeTaskAsync();
// do some lengthy computation
var value = await task.ConfigureAwait(false);
I think it's easy to see that the task could be completed even before the await task call.
In this case the code after the await will be executed in the same context, even if you have used .ConfigureAwait(false).