Reputation: 63720
var task = Task.Run(() => DoSomeStuff()).Result;
What happens here under the hood?
I made a tiny test:
using System;
using System.Threading.Tasks;
public class Program
{
public static void Main()
{
var r = Task.Run( () => {Thread.Sleep(5000); return 123; }).Result;
Console.WriteLine(r);
}
}
It prints "123" after 5s. So does accessing any such property on Task
act as a shortcut to calling Task.Wait()
i.e. is this safe to do?
Previously my code called Task.Delay(5000)
which returned "123" immediately. I fixed this in my question but leave this here as comments and answers reference it.
Upvotes: 0
Views: 1747
Reputation: 659994
You asked two questions. First, does accessing Result
implicitly cause a synchronous Wait
? Yes. But the more important question is:
is this safe to do?
It is not safe to do this.
It is very easy to get into a situation where the task that you are synchronously waiting on has scheduled work to run in the future before it completes onto the thread that you just put to sleep. Now we have a situation where the thread will not wake up until the sleeping thread does some work, which it never does because it is asleep.
If you already know that the task is complete then it is safe to synchronously wait for the result. If you do not, then it is not safe to synchronously wait.
Now, you might say, suppose I know through some other means that it is safe to wait synchronously for an incomplete task. Then is it safe to wait? Well, by the assumption of the question, yes, but it still might not be smart to wait. Remember, the whole point of asynchrony is to manage resources efficiently in a world of high latency. If you are synchronously waiting for an asynchronous task to complete then you are forcing a worker to sleep until another worker is done; the sleeping worker could be doing work! The whole point of asynchrony is to avoid situations where workers go idle, so don't force them to.
An await
is an asynchronous wait. It is a wait that means "wait on running the rest of the current workflow until after this task is done, but find something to do while you are waiting". We did a lot of work to add it to the language, so use it!
Upvotes: 6
Reputation: 131219
The test doesn't wait for Task.Delay()
so it returns immediatelly. It should be :
var r = Task.Run(async () => { await Task.Delay(5000); return 123; }).Result;
The behavior of Result
is well defined - if the task hasn't completed, it blocks until it does. Accessing other Task properties doesn't block
Upvotes: 2
Reputation: 42225
So does accessing any such property on
Task
act as a shortcut to callingTask.Wait()
?
Yes.
From the docs:
Accessing the [
Result
] property's get accessor blocks the calling thread until the asynchronous operation is complete; it is equivalent to calling the Wait method.
However, your test doesn't do what you think it does.
Task.Delay(..)
returns a Task
which completes after the specified amount of time. It doesn't block the calling thread.
So () => { Task.Delay(5000); return 123; }
simply creates a new Task
(which will complete in 5 seconds), then throws it away and immediately returns 123
.
You can either:
Task.Delay(5000).Wait()
(which does the same thing as Thread.Sleep(5000)
)Task
returned from Task.Delay
to complete: Task.Run(async () => { await Task.Delay(5000); return 123; })
Upvotes: 3