I have tons of code written in following manner:
public string SomeSyncOperation(int someArg)
{
// sync code
SomeAsyncOperation(someArg, someOtherArg).ConfigureAwait(false).GetAwaiter().GetResult()
// sync code
};
Here we have some sync code that have to access to async api, so it blocks until results are ready. We can't method change signature and add async
here. So, we are waiting synchronously anyway, so do we need ConfigureAwait(false)
here? I'm pretty sure that we don't, but I'm a bit affraid of removing it because it's probably covers some use cases (or why am I seeing it virtually everywhere? It's just a cargo cult?) and removing this call may lead to some unsafe results.
So does it makes sense at all?
Async is widely considered to be non-blocking.
In async functions, await blocks any code that follows from executing until the Promise has resolves, which means that our refactored code doesn't even start asyncThing2() until asyncThing1() has completed — that's not good.
You can cancel an asynchronous operation after a period of time by using the CancellationTokenSource. CancelAfter method if you don't want to wait for the operation to finish.
Because await is only valid inside async functions and modules, which themselves are asynchronous and return promises, the await expression never blocks the main thread and only defers execution of code that actually depends on the result, i.e. anything after the await expression.
How to correctly block on async code?
You do not correctly block on async code. Blocking is wrong. Asking what the right way is to do the wrong thing is a non-starter.
Blocking on async code is wrong because of the following scenario:
And now you can figure out what goes horribly wrong when you attempt to fetch the result synchronously of the first async operation. It blocks until its child async operation is finished, which will never happen, because now we've blocked the thread that is going to service the request in the future!
Your choices are:
await
the result.There are two ways to write a correct program; writing a synchronous wrapper over an asynchronous function is dangerous and wrong.
Now, you might ask, didn't the ConfigureAwait
solve the problem by removing the requirement that we resume on the current context? That's not the resumption point that we're worried about. If you're going to rely on ConfigureAwait
to save you from deadlock then every asynchronous operation in the stack has to use it, and we don't know if the underlying asynchronous operation that is about to cause the deadlock did that!
If the above is not entirely clear to you, read Stephen's article on why this is a bad practice, and why common workarounds are just dangerous hacks.
https://blog.stephencleary.com/2012/07/dont-block-on-async-code.html
and his updated article giving more hacks and workarounds here:
https://msdn.microsoft.com/en-us/magazine/mt238404.aspx?f=255&MSPPError=-2147217396
But again: the right thing to do is to redesign your program to embrace asynchrony and use await
throughout. Don't try to work around it.
becuase this method has stacktrace of ~20 methods, some of them are implementing some interfaces. Changing it to be async require change declarations in ~50 files, and we convert fully sync interfaces to mixed ones.
Get busy then! This sounds pretty easy.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With