- Stack Overflow Public questions & answers
- Stack Overflow for Teams Where developers & technologists share private knowledge with coworkers
- Talent Build your employer brand
- Advertising Reach developers & technologists worldwide
- About the company
Collectives™ on Stack Overflow
Find centralized, trusted content and collaborate around the technologies you use most.
Q&A for work
Connect and share knowledge within a single location that is structured and easy to search.
Is Task.Result the same as .GetAwaiter.GetResult()?
I was recently reading some code that uses a lot of async methods, but then sometimes needs to execute them synchronously. The code does:
Is this the same as
- 9 From the docs of GetResult : "This type and its members are intended for use by the compiler." Other person shouldn't be using it. – spender Jun 24, 2013 at 20:31
- 47 This is called "sync over async", and unless you know how the task is implemented can be a really bad idea. It can instantly deadlock in many cases (an async / await method in MVC, for example) – Marc Gravell Jun 24, 2013 at 20:35
- 12 Don't Block on Async Code – I4V Jun 24, 2013 at 21:08
- 84 In the real world, we have constructors, we have "no await" interfaces we need to implement, and we are given async methods everywhere. I would be pleased to use something that just works without I have to wonder why it is "dangerous", "not to be used" or "avoid at all costs". Every single time I have to mess with async turn out to a headache. – Larry Oct 22, 2019 at 7:26
- Totally agree Larry. I'm calling a new async microservice from my monolith and using async/await everywhere means I have to update 100+ files. Quite a headache. – wilmol Nov 22, 2022 at 3:43
7 Answers 7
Task.GetAwaiter().GetResult() is preferred over Task.Wait and Task.Result because it propagates exceptions rather than wrapping them in an AggregateException . However, all three methods cause the potential for deadlock and thread pool starvation issues. They should all be avoided in favor of async/await .
The quote below explains why Task.Wait and Task.Result don't simply contain the exception propagation behavior of Task.GetAwaiter().GetResult() (due to a "very high compatibility bar").
As I mentioned previously, we have a very high compatibility bar, and thus we’ve avoided breaking changes. As such, Task.Wait retains its original behavior of always wrapping. However, you may find yourself in some advanced situations where you want behavior similar to the synchronous blocking employed by Task.Wait , but where you want the original exception propagated unwrapped rather than it being encased in an AggregateException . To achieve that, you can target the Task’s awaiter directly. When you write “ await task; ”, the compiler translates that into usage of the Task.GetAwaiter() method, which returns an instance that has a GetResult() method. When used on a faulted Task, GetResult() will propagate the original exception (this is how “ await task; ” gets its behavior). You can thus use “ task.GetAwaiter().GetResult() ” if you want to directly invoke this propagation logic.
“ GetResult ” actually means “check the task for errors” In general, I try my best to avoid synchronously blocking on an asynchronous task. However, there are a handful of situations where I do violate that guideline. In those rare conditions, my preferred method is GetAwaiter().GetResult() because it preserves the task exceptions instead of wrapping them in an AggregateException .
- 4 So basically Task.GetAwaiter().GetResult() is equivalent to await task . I assume the first option is used when the method cannot be marked with async (constructor for instance). Is that correct? If yes, then it collides with the top answer @It'sNotALie – OlegI Oct 11, 2018 at 19:44
- 10 @OlegI: Task.GetAwaiter().GetResult() is more equivalent to Task.Wait and Task.Result (in that all three will block synchronously and have the potential for deadlocks), but Task.GetAwaiter().GetResult() has the exception propagation behavior of await task. – Nitin Agarwal Oct 12, 2018 at 13:01
- 5 @DanielLorenz: See the following quote: "Using ConfigureAwait(false) to avoid deadlocks is a dangerous practice. You would have to use ConfigureAwait(false) for every await in the transitive closure of all methods called by the blocking code, including all third- and second-party code. Using ConfigureAwait(false) to avoid deadlock is at best just a hack). ... the better solution is “Don’t block on async code”." - blog.stephencleary.com/2012/07/dont-block-on-async-code.html – Nitin Agarwal Dec 3, 2018 at 2:30
- 19 I don't get it. Task.Wait and Task.Result are broken by design? Why aren't they made obsolete? – osexpert Dec 26, 2018 at 23:37
- 2 @ScottyMacDev IMO you answered your own question. Sometimes you just can't follow the "best practices". Just be aware of the pitfalls. Consider using continuations with ContinueWith() instead of GetAwaiter().GetResult() . If you need to capture the context you can set TaskScheduler.FromCurrentSynchronizationContext() as a parameter. – Rev Oct 18, 2022 at 11:53
EDIT: This was written when I was 13, and is out of date. I recommend Nitin Agarwal's answer instead.
Pretty much. One small difference though: if the Task fails, GetResult() will just throw the exception caused directly, while Task.Result will throw an AggregateException . However, what's the point of using either of those when it's async ? The 100x better option is to use await .
Also, you're not meant to use GetResult() . It's meant to be for compiler use only, not for you. But if you don't want the annoying AggregateException , use it.
- 28 @JayBazuzi Not if your unit testing framework supports async unit tests, which I think newest versions of most frameworks do. – svick Jun 24, 2013 at 20:52
- 15 @JayBazuzi: MSTest, xUnit, and NUnit all support async Task unit tests, and have for some time now. – Stephen Cleary Jun 24, 2013 at 22:18
- 29 pushing back on the 100x - it's 1000x worse to use await if you're adapting old code and using await requires a rewrite. – stuck Jun 18, 2016 at 17:59
- 17 @AlexZhukovskiy: I disagree . – Stephen Cleary Jul 29, 2016 at 13:53
- 95 The 100x better option is to use await. I hate statements like this, if I could slap await in front of it I would. But, when I'm trying to get async code to work against non-async code like what frequently happens to me a lot in Xamarin, I end up having to use things like ContinueWith a lot in order to make it not deadlock the UI. Edit: I know this is old, but that doesn't alleviate my frustration finding answers that state this with no alternatives for situations where you can't just use await . – Thomas F. Jun 18, 2018 at 13:19
"One last remark: you should avoid using Task.Result and Task.Wait as much as possible as they always encapsulate the inner exception in an AggregateException and replace the message by a generic one (One or more errors occurred), which makes debugging harder. Even if the synchronous version shouldn't be used that often, you should strongly consider using Task.GetAwaiter().GetResult() instead."
- 23 The source referenced here is someone quoting someone else, without a reference. Consider context: I can see lots of people blindly using GetAwaiter().GetResult() everywhere after reading this. – Jack Ukleja Oct 27, 2016 at 1:35
- 2 So we shouldn't use it? – tofutim Feb 17, 2017 at 22:23
- 17 If two tasks end with an exception you will loose the second one in this scenario Task.WhenAll(task1, task2).GetAwaiter().GetResult(); . – Monsignor Aug 11, 2017 at 7:53
- 1 Here's another example: github.com/aspnet/AspNetCore/issues/13611 – George Chakhidze Sep 4, 2019 at 14:48
Another difference is when async function returns just Task instead of Task<T> then you cannot use
I know the example code in the question is for the case Task<T> , however the question is asked generally.
- 1 This is not true. Check out my fiddle which uses exactly this construct: dotnetfiddle.net/B4ewH8 – wojciech_rak Jan 23, 2018 at 14:26
- 5 @wojciech_rak In your code, you are using Result with GetIntAsync() which returns Task<int> not just Task . I suggest you to read my answer again. – Nuri Tasdemir Jan 23, 2018 at 23:33
- 2 You're right, at first I understood you answer that you can't GetFooAsync(...).Result inside a function that returns Task . This now makes sense, since there are no void Properties in C# ( Task.Result is a property), but you can of course call a void method. – wojciech_rak Jan 24, 2018 at 14:02
- 1 The Task is not returning a value so we expect .Result to be an error. The fact that task.GetAwaiter().GetResult() still works is counter-intuitive and deserves a little emphasis. – H2ONaCl Jan 30, 2021 at 5:26
As already mentioned if you can use await . If you need to run the code synchronously like you mention .GetAwaiter().GetResult() , .Result or .Wait() is a risk for deadlocks as many have said in comments/answers. Since most of us like oneliners you can use these for .Net 4.5<
Acquiring a value via an async method:
Syncronously calling an async method
No deadlock issues will occur due to the use of Task.Run .
Could cause a deadlock if the calling thread is from the threadpool. The following happens: A new task is queued to the end of the queue, and the threadpool thread which would eventually execute the Task is blocked until the Task is executed.
- If you vote down, please say why. Hard to improve answers otherwise. – Ogglas Dec 30, 2020 at 23:51
- 2 Why does it prevent a deadlock? I realize that Task.Run offloads the work to ThreadPool , but we're still waiting on this thread for that work to finish. – Mike Feb 8, 2021 at 8:47
- 1 @Mike The problem with using only .Result or .Wait() is that if you block the threads which are supposed to work on the Tasks, then there won’t be a thread to complete a Task. You can read more about it here: medium.com/rubrikkgroup/… – Ogglas Feb 8, 2021 at 9:17
I checked the source code of TaskOfResult.cs ( Source code of TaskOfResult.cs) :
If Task is not completed, Task.Result will call Task.Wait() method in getter .
If we call the GetAwaiter method of Task , Task will wrapped TaskAwaiter<TResult> ( Source code of GetAwaiter() ), ( Source code of TaskAwaiter ) :
And if we call the GetResult() method of TaskAwaiter<TResult> , it will call Task.Result property, that Task.Result will call Wait() method of Task ( Source code of GetResult() ):
It is source code of ValidateEnd(Task task) ( Source code of ValidateEnd(Task task) ):
This is my conclusion:
As can be seen GetResult() is calling TaskAwaiter.ValidateEnd(...) , therefore Task.Result is not same GetAwaiter.GetResult() .
I think GetAwaiter().GetResult() is a better choice instead of .Result because the latter doesn't wrap exceptions.
I read this at page 582 in C# 7 in a Nutshell (Joseph Albahari & Ben Albahari).
If an antecedent task faults, the exception is re-thrown when the continuation code calls awaiter.GetResult() . Rather than calling GetResult , we could simply access the Result property of the antecedent. The benefit of calling GetResult is that if the antecedent faults, the exception is thrown directly without being wrapped in AggregateException , allowing for simpler and cleaner catch blocks.
Source: C# 7 in a Nutshell's page 582
If a task faults, the exception is re-thrown when the continuation code calls awaiter.GetResult(). Rather than calling GetResult, we could simply access the Result property of the task. The benefit of calling GetResult is that if the task faults, the exception is thrown directly without being wrapped in AggregateException, allowing for simpler and cleaner catch blocks. For nongeneric tasks, GetResult() has a void return value. Its useful function is then solely to rethrow exceptions.
source : c# 7.0 in a Nutshell
Not the answer you're looking for? Browse other questions tagged c# async-await or ask your own question .
- The Overflow Blog
- How Intuit democratizes AI development across teams through reusability sponsored post
- The nature of simulating nature: A Q&A with IBM Quantum researcher Dr. Jamie...
- Featured on Meta
- We've added a "Necessary cookies only" option to the cookie consent popup
- Launching the CI/CD and R Collectives and community editing features for...
- The [amazon] tag is being burninated
- Temporary policy: ChatGPT is banned
- Staging Ground Beta 1 Recap, and Reviewers needed for Beta 2
Hot Network Questions
- Are the plants animated by an Assassin Vine considered magical?
- Forced to pay a customs fee for importing a used wedding dress into the Netherlands. Is there a way to avoid paying?
- Displaying label if field contains 'X' or 'Y' value in QGIS
- Imtiaz Germain Primes
- What is this movie where a woman is attacked and possessed by a worm
- Serialization and deserialization doubly-linked list with pointer to random node on c++
- Rolling cube on an infinite chessboard
- Are there tables of wastage rates for different fruit and veg?
- MAE to find tuning parameter for lasso logistic regression
- Are people inherently good or bad according to Judaism
- Which type of license doesn't require attribution in Github projects?
- Is there a single-word adjective for "having exceptionally strong moral principles"?
- Why do academics stay as adjuncts for years rather than move around?
- How do/should administrators estimate the cost of producing an online introductory mathematics class?
- Why are all monasteries human?
- Align vertically 2 circuits
- Blender python - set location does not use centimeters
- My contour integrations seem to contradict the residue theorem.
- How to sample a complex function?
- Why did Windows 3.0 fail in Japan?
- How do you ensure that a red herring doesn't violate Chekhov's gun?
- Earn CC flight miles by paying rent?
- Why isn't light switch turning off the outlet?
- Is it suspicious or odd to stand by the gate of a GA airport watching the planes?
Why is .GetAwaiter().GetResult() bad in C#?
Why is .GetAwaiter().GetResult(), or .Wait() or .Result bad? It ends up boiling down to deadlocks and threadpool starvation. This post gives a gentle, high up look at why this may happen.
For the purposes of this not-so-in-depth post, consider the following the same evil:
There is nuance between them but feel free to read about them on your own.
The Documentation Says No
GetAwaiter() returns a TaskAwaiter object and if we peek into the documentation it says:
"This type is intended for compiler use only."
The Documentation Says No, Again
The GetResult() documentation has been updated since this post was originally written. But in the PR to update said documentation , Stephen Toub from Microsoft outlines more of the "No":
Now, there's the related question of "what about GetAwaiter().GetResult() on a Task rather than on a configured awaiter, as those docs also say the same thing". When all of this support was introduced, the intent was in fact that no one should ever be using these directly, that they were purely for compiler consumption.
Deadlocks Can Happen
There are some very smart people with some very good answers but in short deadlocks happen because:
- The thread doing the work is now blocked due to a .Result and waiting for the call to come back.
- When the async task returns from the work, there's no thread to complete the work as it is stuck at .Result .
It's a little more complex due to SynchronizationContext and how .NET Core runs differently from an ASP.NET application which runs differently from a regular console application. Linking again, this fantastic writeup by Eke Péter goes much more in depth.
"But I've Never Seen a Deadlock Happen!"
Throw enough calls to simulate a high workload or call from a UI thread.
Now that we get the gist of how deadlocks happen, we can apply this to a threadpool. If we run enough load and have enough threads in the pool waiting for their own .Result calls, then eventually there will be no thread left to actually do the returned work.
"I'm using HttpClient (or other popular class/library) from synchronous code, can I just use .Result?"
Nope, same reasons as above. Sure in your weekend project it might be fine, but at 5:15pm on a Friday when there's a deadlock and you're trawling through error logs and memory dumps, maybe not. But if that's what you're into, I'm not shaming.
Alternatively if you'd like to make an HTTP request I'd like to think you have two options when you're currently using HttpClient in a synchronous codebase. I'm sure there are more, but these are easy to argue:
- Use another way to perform the HTTP request. Either a third party library or an older .NET way such as HttpWebRequest.
- Embrace the viral nature of async and let it propagate through your code.
The Quickest Example...
...With the least setup that I could get going. Credit to the example in this post that helped me a smidge.
- In Visual Studio, create a new MVC project
- Have your HomeController look like the code below
The default index page should not load, and the browser will be left waiting.
It's rough when you're trying to integrate an async piece of code into an existing (possibly legacy) synchronous codebase but I hope that this light brush of knowledge will help you understand why it can be scary to blindly throw around .GetAwaiter().GetResult() or any other async blocking call.
There is a lot more to dive into too, especially around SynchronizationContext and ConfigureAwait(false) . Try a good bite into those if you're hungry for more understanding.
If you enjoyed this post or it helped you out 😄
Sign up for more like this..
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:
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.
If this was useful, follow me on Twitter (@gsferreira) and let's keep in touch!
Sunday, june 30, 2019, task.wait() vs task.getawaiter().getresult().
- Task.Wait() (or Task.Result to get the return value if it returns something)
Post a comment.
A Tour of Task, Part 6: Results
The task members discussed in this blog post are concerned with retrieving results from the task. Once the task completes, the consuming code must retrieve the results of the task. Even if the task has no result, it’s important for the consuming code to examine the task for errors so it knows whether the task completed successfully or failed.
The Result member only exists on the Task<T> type; it does not exist on the Task type (which represents a task without a result value).
Like Wait , Result will synchronously block the calling thread until the task completes. This is generally not a good idea for the same reason it wasn’t a good idea for Wait : it’s easy to cause deadlocks .
Furthermore, Result will wrap any task exceptions inside an AggregateException . This usually just complicates the error handling.
Speaking of exceptions, there’s a member specifically just for retrieving the exceptions from a task:
Unlike Result and Wait , Exception will not block until the task completes; if called while the task is still in progress, it will just return null . If the task completes successfully or is cancelled, then Exception will still return null . If the task is faulted, then Exception will return the task’s exceptions wrapped in an AggregateException . Again, this usually just serves to complicate the error handling.
The GetAwaiter member was added to Task and Task<T> in .NET 4.5, and it’s available as an extension method on .NET 4.0 using the Microsoft.Bcl.Async NuGet package. Normally, the GetAwaiter method is just used by await , but it is possible to call it yourself:
The code above will synchronously block until the task completes. As such, it is subject to the same old deadlock problems as Wait and Result . However, it will not wrap the task exceptions in an AggregateException .
The code above will retrieve the result value from a Task<T> . The same code pattern can also be applied to Task (without a result value); in this case “GetResult” actually means “check the task for errors”:
In general, I try my best to avoid synchronously blocking on an asynchronous task. However, there are a handful of situations where I do violate that guideline. In those rare conditions, my preferred method is GetAwaiter().GetResult() because it preserves the task exceptions instead of wrapping them in an AggregateException .
Of course, await is not a member of the task type; however, I feel it’s important to remind today’s readers that the best way of retrieving results from a Promise Task is to merely use await . await retrieves task results in the most benign manner possible: await will asynchronously wait (not block); await will return the result (if any) for a successful task; and await will (re-)throw exceptions for a failed task without wrapping them in an AggregateException .
In short, await should be your go-to option for retrieving task results. The vast majority of the time, await should be used instead of Wait , Result , Exception , or GetAwaiter().GetResult() .
- AsyncState and CreationOptions
- Delegate Tasks
- Promise Tasks
- Async/await Intro
- There Is No Thread
- Don't Block on Async Code
- React/Redux TodoMVC
- A Tour of Task
- Task.Run Etiquette
- Task.Run vs. BackgroundWorker
- TCP/IP .NET Sockets FAQ
- Managed Services
- IDisposable and Finalizers
- Option Parsing
- No suggested jump to results
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement . We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Using Result instead of GetAwaiter().GetResult() #13611
Kahbazi commented Sep 1, 2019
Eilon commented Sep 1, 2019
Sorry, something went wrong.
RemiBou commented Sep 2, 2019
Kahbazi commented sep 2, 2019 • edited.
- 👍 1 reaction
vertonghenb commented Sep 2, 2019 • edited
Kahbazi commented sep 2, 2019.
pranavkm commented Sep 2, 2019
Eilon commented sep 3, 2019.
No branches or pull requests
This browser is no longer supported.
Upgrade to Microsoft Edge to take advantage of the latest features, security updates, and technical support.
Task. Get Awaiter Method
Some information relates to prerelease product that may be substantially modified before it’s released. Microsoft makes no warranties, express or implied, with respect to the information provided here.
Gets an awaiter used to await this Task .
An awaiter instance.
This method is intended for compiler use rather than use directly in code.
Why do Task.Wait and Task.Result even exist?
Both Task.Wait and Task.Result are blocking and may also cause deadlocks and on top of that they also wrap exceptions in an AggregateException .
Now if you are in a situation where you can't use async/await and you have to do sync over async, the preferred way to do it seems to be Task.GetAwaiter().GetResult(); which can still cause deadlocks but at least it doesn't wrap exceptions in an AggregateException .
So why do Task.Wait and Task.Result even exist? These methods seem wrong by design to me. Am I missing something?
What you are missing is that not all tasks are asynchronous operations. As a matter of fact Tasks were originally created to represent parallel operations.
not all tasks are asynchronous
Much to the despise of developers born in a year starting with a 2.
That would explain why it was called the Task Parallel Library
Yeah I see that now after reading many answers. I started working with C# only a few months ago and so I wasn't aware of the origins of Tasks. Thanks for the info!
Man, this dev is going to have trip when get into semaphore and parallel tasks that have synchronous operating tasks.
Well first off, the task class is used with async/await for asynchronous code but it's also used for parallel programming with the TPL and other related libraries. Task in those worlds is a kind of higher level abstraction above threads and the thread pool for doing CPU bound parallel work, which while parallel is still synchronous and so requires the members you spoke about.
Also the .net team are pragmatic about things, they accept that sometimes you need to make sync and async mix
It annoys me that people are down-voting you. This is 100% the right answer and why people still need to be taught how to use it correctly.
Tasks are useful for working with...
Task hierarchies. (Look into how a task can be the child of another task)
Long running operations (replaces Thread)
Short running operations (replaces ThreadPool)
Asynchronous operations (replaces the Begin/End method pairs)
Custom thread schedulers
And probably a whole lot more that I've not even heard of.
This is the real answer. In short, Task.Result and Task.Wait() existed before async/await .
In hindsight, I wish they'd required an explicit transition between Task and async rather than re-using Task .
As much as I hate it, some people (especially people working in legacy code) need an escape hatch. These are people who have dotted all the 'i's, crossed all the 't's, understand the risks, and can't do their work without it.
The main problem is the escape hatch is sitting there in plain sight where newbies can be tempted by it. I've been confused a couple of times when auto-complete gave me a Task.WaitAll() instead of Task.WhenAll() and thought I was going crazy. I think burying it under something obscure like .GetAwaiter().GetResult() is the way to go.
Which is, of course, why 99% of await calls outside of ASP .NET Core should have .ConfigureAwait(false) , it's the 90/10 rule: make your default cover the 10% so 90% of peoples' time is spent explaining your API!
They were created before await , so not GetAwaiter was implemented.
If a task has completed, you can get Task.Result as many times as you want and it will give the result.
This can often be useful. One example is when using ContinueWith.
You still have to use .Result to get the result after awaiting .WhenAll or .WhenAny to await the completion of multiple parallel tasks.
Unless you're using a ValueTask you could just use the await syntax to unwrap it, even on completed tasks.
There are a few rare scenarios where you could use Task.Wait, but they are rare. Stephen Cleary has good articles on the topic. https://blog.stephencleary.com/2014/10/a-tour-of-task-part-5-wait.html
Because the Task class comes from the TPL in .NET Fx 4, whereas async/await came with 4.5. I wish they’d made an all-new type, but too late for that.
If they made an all new type, then every API would have to be duplicated. And there would be people complaining that they can't await a parallel operation.
Because you can do
And documentation for Task<T> class has examples of using .Result : https://docs.microsoft.com/en-us/dotnet/api/system.threading.tasks.task-1?view=net-6.0
Wait and Result don't cause a deadlock on their own: a synchronization context has to be involved; otherwise all continuations are just pushed onto the default threadpool.
Simply, because there are some situations where you have no choice.
And it’s too long for me to post what those situations are. It relies on common sense, an understanding of the risks and context for which you operate on.
If anyone has a solution to this, I am all ears!
I use Task.GetAwaiter().GetResult(); when I run cmd processes.
For example, using the great Cli.Wrap library:
If you run this in a console app, the console app will complete before the build is completed, and the build will not be completed:
Or some other cmd process like a Resharper or CSharpier - the console app will exit before the command is done, even if you await the command .
If I use the Task.GetAwaiter().GetResult(); then the task will be forced to 'lock' the program execution's on that thread, and the console app will not exit until the cmd command is complete.
Does anyone know how of this can be solved without Task.GetAwaiter().GetResult(); ? Thanks!
Is your Main function declared as async Task or async void ? I ask because the latter will cause the symptoms you describe.
null reference exceptions
Oct 22, 2022
Awaiting any type in C#
If you regularly use the async/await feature in C# (Let’s be honest, who doesn’t), you are probably familiar with Task and Task<T>.
Even more so, if you work with a C# version ≥ C# 8, you might even have worked with ValueTask before.
What do these have in common? Pretty straightforward, you can await them to control the flow of your applications logic. You might be inclined to think that this is compiler magic, but it isn’t — After all, the mentioned types are simply types like any other, they just seem to have something special about them which allows them to be “awaited”.
If we take a look at the C# specifications, there is a paragraph dedicated to awaitable expressions. For now, let’s skip over the possibility to await a dynamic type.
So, what does an awaitable type need? An accessible instance or extension method (This will come in handy later) of GetAwaiter without any parameter, returning a compatible type T.
The compatible type in return needs to implement
- The INotifyCompletion (and usually ICriticalNotifyCompletion) interface, which introduces an OnCompleted method. This method will be invoked to pass your awaitable type the continuation it should execute once it is ready. If you want to know more about the difference between INotifyCompletion and ICriticalNotifyCompletion, check out Stephen Toubs blog !
- A property named IsCompleted. This sounds a bit odd, since we just covered the method which is responsible for queueing a continuation. So why do we need explicit control? It’s pretty simple, the async state machine will only queue a continuation, if it determines that the unit of work which takes place did not complete yet. Therefore, we need this property to figure this out.
- A GetResult method to grab the result of the awaitable once completed. The return type of this method is variable, and determines what the type of the result will be!
So, you probably never had to deal with all of this yet and you still use async/await all the time, so who takes care of this for the default case?
You usually await a Task or Task<T> , so the first thing to check is how it fulfills the first requirement of exposing a GetAwaiter method with the required functionality.
Looking at source.dot.net, here it is:
So for the default case, there seems to be a default TaskAwaiter to cover the rest of the requirements for us. And in fact, the implementation has as well need. It accepts a Task and exposes:
- IsCompleted, pointing at the completion state of the Task itself
- OnCompleted, which internally queues the continuation onto the underlying Task (Feel free to dig in further — The further implementation is mostly concerned with how the continuation should continue its flow, which can vary wildly depending on how your application context handles the synchronization context flows)
- GetResult, which evaluates the underlying Task. Note that it is completely legal to throw here as well, in case the unit of work failed.
So, with all of this knowledge, we can try to rebuild our own “awaiter”.
This example might be a bit goofy, but let’s attempt to write a custom class which queries google.com after a two second delay, and can be awaited.
Let’s start with a simple class setup:
Pretty straightforward so far. Now we need to tick off the boxes of the awaitable pattern. First, we need to implement our own Awaiter:
This is essentially very similar to a TaskAwaiter. We implement ICriticalNotifyCompletion, which forces us to implement OnCompleted and UnsafeOnCompleted. We simply queue the continuation through ContinueWith on the task itself, and wrap up the other two methods by simply delegating onto the Task.
Now, let’s write our Main method to setup the whole invocation:
However, as of now, we still receive an error, since there is one more thing to cover: We need to add the GetAwaiter() to the GoogleQuerier class!
Now everything compiles, and if we check our output:
Great, everything worked out! Our GoogleQuerier is now awaitable, works as expected (We wait for 2 seconds and then get the result)
How is this useful to me?
Arguably, you will probably never have any need to write your own awaiter, but I still wanted to cover the concept. TaskAwaiter usually does the job very well.
What’s more powerful about the whole concept is the knowledge that you can make any type awaitable by implementing the GetAwaiter method. It’s especially great once you remember that you don’t have to make it an instance method, but you can also use an extension method, so there is no restriction, you can even extend framework classes!
Let’s start with a simple one: If you have a Lazy<Task<T>>, it’s a bit annoying to constantly write
So, why don’t we just write a small extension method to make it easier?
Recently I also dealt with a situation, in which a method received a CancellationToken I wanted to “await”, but this is not possible out of the box. So, would you add another parameter with the CancellationToken cancellation time? We can also simply write an extension to make a CancellationToken awaitable until it ran out!
As you can see, we can also simply build our own Task within the GetAwaiter and just return awaiters as we like, as long as the signature matches.
Other smooth things we can do is to make the usage of Task.WhenAll more convenient. Let’s implement awaitable tuples and lists!
Ultimately I think custom awaitables are really fun. They might not be a feature one should put up everywhere, but there are situations where it can be really useful (The tuple one is actually one I tend to use day to day!). It is definitely one of the duck-typing features which just feels nice to play around with.
More from ITNEXT
ITNEXT is a platform for IT developers & software engineers to share knowledge, connect, collaborate, learn and experience next-gen technologies.
About Help Terms Privacy
Get the Medium app
26 year old full stack software developer from Germany - Enthusiatic about C#, .Net and Cloud — Visit me at https://dotschranz.net/
Text to speech
An organization’s task environment is the collection of factors that affects its ability to achieve goals. Common factors in the task environment include competitors, customers, suppliers and distributors.
Task interdependence sets rules and guidelines for the sharing of expertise, materials and information between members of an organization working on interdependent tasks.
Task Force is a house brand of home improvement store Lowes and is considered to be a budget brand of tools, similar in quality to Blue Hawk and beneath Kobalt. Lowes partnered with LG Sourcing Inc. to make Task Force tools.
@OlegI: Task.GetAwaiter().GetResult() is more equivalent to Task.Wait and Task.Result (in that all three will block synchronously and
GetAwaiter().GetResult(), or .Wait() or .Result bad? It ends up boiling down to deadlocks and threadpool starvation. This post gives a gentle
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
But when we are using Task.GetAwaiter().GetResult(), it will throw the exception directly which will make things like debugging/logging easy.
GetResult() because it preserves the task exceptions instead of wrapping ... be used instead of Wait , Result , Exception , or GetAwaiter().
In CorsMiddleware there's a GetAwaiter().GetResult() call on a task. Since the task is completed successfully isn't it better to call Result
task.Wait();. • return task.GetAwaiter().GetResult();.
Threading.Tasks). Queues the specified work to run on the ThreadPool and returns a task or Task<TResult> handle for that work.
GetAwaiter().GetResult(); which can still cause deadlocks but at least it doesn't wrap exceptions in an AggregateException . So why do Task.
"@i3arnon Thanks for the hint! I have measured .Result vs .Wait vs GetAwaiter.GetResult() and it seems that for Tasks the GetAwaiter
You usually await a Task or Task<T>, so the first thing to check is how it ... IsCompleted;public string GetResult() => _queryTask.Result;