Avoid GetAwaiter().GetResult() at all cost
When you need to wait for a Task, are you using ".GetAwaiter().GetResult()" instead of ".Result" and ".Wait()" when you are in a synchronous method? You are doing the correct thing! But only if you can't change that method!
In case you don't know, in C#, you should always aim to work with async/await when you have Tasks. You should go all way down with async/await.
If you are using ".GetAwaiter().GetResult()", ".Result" or ".Wait()" to get the result of a task or to wait for the task completion you may experience deadlocks or thread pool starvation.
But, sometimes ".GetAwaiter().GetResult()" is presented as a good way to replace ".Result" and ".Wait()" when we can't use async/await. That idea can be dangerous. I agree that this is a far better solution then ".Result" and ".Wait()" because the error handling will be much better. The stack trace of a given expression will be much cleaner. But, under the wood, ".GetAwaiter()" is relying on ".Wait()", so you may experience the deadlocks or thread pool starvation.
So, what I have to recommend you is to avoid at all cost using ".GetAwaiter().GetResult()", ".Result" or ".Wait()". Refactor your code from top to bottom to use async/await. Use it as a code smell that something can go wrong under stress. You will see that under stress, the system will behave so much better.
What can I do if I can't use Async/Await?
That's a fair question. Sometimes we need to go with "Sync over async".
Two simple scenarios are when:
- You have a Task in a constructor; - Need to respect an interface that you don't control;
In this situation, I recommend you to use ".GetAwaiter().GetResult()" only once. Extract a private async method that your public method can relly on.
Now, It's time to review your codebase and to spread the word!
Also, I recommend you to install the Threading Analyzer (Microsoft.VisualStudio.Threading.Analyzers). It will help you to spot potential problems.