There is more to it.
Here are some reasons why yous should stick with async/await
1. You're not meant to use GetResult
TaskAwaiter.GetResult Method states
This API supports the product infrastructure and is not intended to be used directly from your code.
2. Without await
your code is not longer asynchronous
By dropping await
you're getting rid of the async magic and your code becomes synchronous and blocking and each request uses a thread until it's finished.
Lot's of work, not many threads.
I hope this little example will be able to showcase how easy it is for a framework to deal with more requests then there are threads when Tasks
are used.
Think of it as 3 waiters being able to serve 10 tables.
static async Task DoAsync(int index)
{
var threadId = Thread.CurrentThread.ManagedThreadId;
await Task.Delay(TimeSpan.FromMilliseconds(index*100));
Console.WriteLine($"task: {index}, threads: {threadId} -> {Thread.CurrentThread.ManagedThreadId}");
}
public static async Task Main()
{
Console.WriteLine($"thread: {Thread.CurrentThread.ManagedThreadId}: Main");
var tasks = Enumerable.Range(0, 10).Select( i => DoAsync(i));
await Task.WhenAll(tasks);
Console.WriteLine($"thread: {Thread.CurrentThread.ManagedThreadId}: Main Done");
}
Output
thread: 1: Main
task: 0, threads: 1 -> 1
task: 1, threads: 1 -> 4
task: 2, threads: 1 -> 4
task: 3, threads: 1 -> 4
task: 4, threads: 1 -> 4
task: 5, threads: 1 -> 4
task: 6, threads: 1 -> 5
task: 7, threads: 1 -> 5
task: 8, threads: 1 -> 5
task: 9, threads: 1 -> 5
thread: 5: Main Done
As you found yourself, the point of n
threads being able to handle x>n
request is explicitly expressed in Async in depth's 'What does this mean for a server scenario?' section.
This model works well with a typical server scenario workload. Because there are no threads dedicated to blocking on unfinished tasks, the server threadpool can service a much higher volume of web requests.
(...)
State machines
Asynchronous programming is a good source of information about async/await. There are two passages especially relevant to the simplified view of await == GetAwaiter().GetResult()
.
What happens under the covers
There's a lot of moving pieces where asynchronous operations are concerned. If you're curious about what's happening underneath the covers of Task and Task, checkout the Async in-depth article for more information.
On the C# side of things, the compiler transforms your code into a state machine which keeps track of things like yielding execution when an await is reached and resuming execution when a background job has finished.