I have a theoretical question to you. What happens if I await the Result of a Task inside another task? I want to know if my current system will work afterwards.
A task gets launched and does some stuff. At some point that task might need another one in order to process data which the current task is not able to process by itself. So I use await to ensure that the current task won't continue as long as he has not the result of the helper task. But what happens if the helper fails? Will the current task remain locked?
Can I avoid this deadlock somehow (without changing the system itself - task inside task)?
A task gets launched and does some stuff. At some point that task might need another one in order to process data which the current task is not able to process by itself. So I use await to ensure that the current task won't continue as long as he has not the result of the helper task. But what happens if the helper fails? Will the current task remain locked?
The core idea behind async
and await
is that asynchronous code works just about the same as synchronous code.
So, if you have synchronous code like this:
void HelperMethod()
{
throw new InvalidOperationException("test");
}
void DoStuff()
{
HelperMethod();
}
then you would expect DoStuff
to propagate the InvalidOperationException
from the helper method. Similarly, that's what happens with asynchronous code:
async Task HelperMethodAsync()
{
throw new InvalidOperationException("test");
}
async Task DoStuffAsync()
{
await HelperMethodAsync();
}
That is, DoStuffAsync
will also propagate the InvalidOperationException
.
Now, it doesn't work exactly the same way, of course, since it must be asynchronous, but the general idea is that all your control flow such as try
/catch
, for
loops, etc, all "just work" for asynchronous code very similarly to synchronous code.
What's actually going on is that when HelperMethod
ends with an InvalidOperationException
, the exception is caught and placed on the returned Task
, and the task is completed. When the await
in DoStuffAsync
sees that the task has completed, it examines its exceptions and re-raises the first one (in this case, there is only one, the InvalidOperationException
). And it re-raises it in a way that preserves the call stack on the exception. This in turn causes the Task
returned from DoStuffAsync
to be completed with that same exception.
So, under the covers async
and await
are doing a bit of work to ensure that you can just call other methods with await
and use try
/catch
the same way as you would in synchronous code. But most of the time you don't have to be aware of that.
What happens if I await the Result of a Task inside another task?
You can only await
a Task.Result
if it is an awaitable
(meaning it has a GetAwaiter
method). This is rarely the case. I assume you mean await
on an inner Task.
But what happens if the helper fails? Will the current task remain locked?
First, im not sure what you mean by "locked". The task isn't locked while you await
on the inner task. Control is yielded back to the calling method until that inner task completes. If that inner task fails and you fail to properly handle that exception, you parent task will fault as well. You need to make sure you handle exceptions gracefully:
var task =
Task.Run(async () =>
{
try
{
await AsyncHelper.DoSomethingAsync();
}
catch (Exception e)
{
// Handle exception gracefully
}
});
If you await
on the parent Task, you'll notice the inner exception propogating from the unhandled inner Task.
Can I avoid this deadlock somehow?
I see no reason why your code should deadlock in this specific case. Not sure why this worries you.
It's really easy to test. For example:
[TestMethod, ExpectedException(typeof(Exception))]
public async Task DoFaultedTaskThrowOnAwait()
{
var task = Task.Factory.StartNew(() => { throw new Exception("Error 42"); });
await task;
}
[TestMethod, ExpectedException(typeof(AggregateException))]
public void DoFaultedTaskThrowOnWait()
{
var task = Task.Factory.StartNew(() => { throw new Exception("Error 42"); });
task.Wait();
}
Both tests pass, notice that Wait
throws an AggregateException
, and await
throws the Exception
.