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.
Related
I have a minimal example of some async code that is exhibiting strange behavior. This is sandbox code, more geared at trying to understand async better --
private async Task ExhibitStrangeBehaviorAsync()
{
async Task TaskA()
{
await Task.Run(async () =>
{
throw new Exception(nameof(TaskA));
await Task.Yield();
});
}
async Task TaskB()
{
await Task.Run(() =>
{
throw new Exception(nameof(TaskB));
});
}
var tasks = new List<Task>
{
TaskA(),
TaskB(),
};
var tasksTask = Task.WhenAll(tasks);
try
{
await tasksTask;
}
catch
{
Debug.WriteLine(tasksTask.Exception.Message);
}
}
Intermittently, this code will hang. I would like to better understand why. My guess currently is the intermittent nature is due to the out-of-order execution of the aggregated tasks, and/or this line from Asynchronous Programming:
Lambda expressions in LINQ use deferred execution, meaning code could end up executing at a time when you're not expecting it to.
TaskA would fall in this category.
The code does not seem to hang if TaskB also Task.Runs an async lambda, or if neither local Task function contains Task.Run, e.g.
async Task TaskA()
{
//await Task.Run(async () =>
//{
throw new Exception(nameof(TaskA));
await Task.Yield();
//});
}
async Task TaskB()
{
//await Task.Run(() =>
//{
throw new Exception(nameof(TaskB));
//});
}
Can anybody shed some light on what's going on here?
EDIT
This is executing in the context of a UI thread, specifically that of a Xamarin.Forms application.
EDIT 2
Here is another variant on it that runs straight out of the Xamarin.Forms OnAppearing lifecycle method. I had to modify TaskA/B slightly, though they break this way with the original setup above as well.
protected async override void OnAppearing()
{
base.OnAppearing();
async Task TaskA()
{
await Task.Run(async () =>
{
throw new InvalidOperationException();
await Task.Delay(1).ConfigureAwait(false);
}).ConfigureAwait(false);
}
async Task TaskB()
{
await Task.Run(() => throw new ArgumentException()).ConfigureAwait(false);
}
var tasks = new List<Task>
{
TaskA(),
TaskB(),
};
var tasksTask = Task.WhenAll(tasks);
try
{
await tasksTask;
}
catch
{
Debug.WriteLine(tasksTask.Exception.Message);
}
}
There is some chance this may be related to another issue I have had - I am using an older version of Xamarin.Forms, one which has problems with its OnAppearing handling async correctly. I am going to try with a newer version to see if it resolves the issue.
I'm going to guess you're calling ExhibitStrangeBehaviorAsync().Wait() in a GUI app (or similar), as that's the only situation which I think of which can cause a deadlock here. This answer is written on that assumption.
The deadlock is this one, cause by the fact that you're running an await on a thread which has a SynchronizationContext installed on it, and then blocking that same thread higher up the callstack with a call to .Wait().
When you run TaskA() and TaskB(), both of those methods post some work to the ThreadPool, which takes a variable amount of time. When the ThreadPool gets around to actually executing the throw statements, this causes the Task returned from TaskA / TaskB to complete with an exception.
tasksTask will complete when the two tasks returned from TaskA and TaskB complete.
The race comes from the fact that, at the point that this line is executed:
await tasksTask;
the task tasksTask may or may not have completed. tasksTask will have completed if the Tasks returned from TaskA and TaskB have completed, so it's a race against the main thread's progression towards the await tasksTask line, against how fast the ThreadPool can run both of those throw statements.
If tasksTask is completed, the await happens synchronously (it's smart enough to check whether the Task being awaited has already completed), and there's no chance of a deadlock. If tasksTask hasn't completed, then my guess is that you're hitting the deadlock described here.
This is also consistent with your observation that removing the calls to Task.Run removes the deadlock. In this case, the tasks returned from TaskA and TaskB complete synchronously as well, so there's no race.
The morale of the story is, as is oft-repeated, do not mix async and sync code. Do not call .Wait() or .Result on a Task which is influenced in any way by await. Also consider tactical use of .ConfigureAwait(false) to guard against other people calling .Wait() on tasks which you produce.
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 have just tested something that I was sure would fail miserably, but to my surprise, it worked flawlessly, and proves to myself that I am still quite mystified by how async-await works.
I created a thread, passing an async void delegate as the thread's body.
Here's an oversimplification of my code:
var thread = new Thread( async () => {
while( true ) {
await SomeLengthyTask();
...
}
});
thread.Start();
thread.Join();
The thing is that as far as I understand, when the execution hits the await keyword, there is an implicit return from the method, in this case the body of the looping thread, while the rest of the code is wrapped in a callback continuation.
Because of this fact, I was pretty sure that the thread would terminate as soon as the await yielded execution, but that's not the case!
Does anybody know how this magic is actually implemented? Is the async functionality stripped down and the async waits synchronously or is there some black magic being done by the CLR that enables it to resume a thread that has yielded because of an await?
The thread is indeed terminated very quickly.
But since the Thread constructor doesn't accept an async lambda what you got there is an async void delegate.
The original thread will end and the continuation (the rest after the await) will be posted to the ThreadPool and eventually run on another thread.
You can test that by checking the thread id:
var thread = new Thread(async () =>
{
while (true)
{
Console.WriteLine(Thread.CurrentThread.ManagedThreadId);
await SomeLengthyTask();
Console.WriteLine(Thread.CurrentThread.ManagedThreadId);
}
});
thread.Start();
thread.Join();
Console.ReadLine();
Output:
3
5
5
...
5
To make the example simpler let's assume you have this Run method:
void Run(Action action)
{
action();
}
And you call it with your async delegate
Run(async () =>
{
while(true)
{
await SomeLengthyTask();
...
}
});
The execution of Run will complete almost immediately when it reaches the first await and returns. The rest of the async delegate will continue on the ThreadPool with another thread.
Generally, each time you reach an await in the execution of an async method the thread is lost and the continuation (the rest after the awaited task completes) will be posted to the ThreadPool (unless if there's a SynchronizationContext present, like in the UI thread). It may be that it execution will be on the same thread (as in my example with 5) but it also may not.
In your case the thread you create explicitly isn't part of the ThreadPool so it will definitely be terminated and the rest will run on a different thread.
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?"
My problem is that when a Task has a Task.WhenAll() call (running other Tasks) the line of WhenAll() makes the consuming code continue execution, unlike what I would expect. So the following code outputs "finished" immediately when the Task.WhenAll() is hit, not after all the tasks in its argument are finished.
// Just a simple async method
public Task DoWorkAsync()
{
return Task.Factory.StartNew(
() =>
{
// Working
});
}
// This one used the previous one with Task.WhenAll()
public Task DoLoadsOfWorkAsync()
{
return Task.Factory.StartNew(
async () =>
{
// Working
// This line makes the task return immediately
await Task.WhenAll(DoWorkAsync(), DoWorkAsync());
// Working
});
}
// Consuming code
await DoLoadsOfWorkAsync();
Console.WriteLine("finished");
I'd expect the WriteLine() to be called when the last line of DoLoadsOfWorkAsync() is executed.
What am I doing wrong? Thanks in advance.
Task.WhenAll returns a new Task immediately, it does not block. The returned task will complete when all tasks passed to WhenAll have completed.
It is an asynchronous equivalent to Task.WaitAll, and this is the method to use if you want to block.
However you have another problem. Using Task.Factory.StartNew and passing an async delegate seems to lead to a type of Task<Task> where the outer task completes when the inner task starts to execute (rather than when it has completed).
Using the newer Task.Run avoids this.