How to properly block asynchronous code?

I have tons of code written like this:

public string SomeSyncOperation(int someArg)
{
   // sync code 
   SomeAsyncOperation(someArg, someOtherArg).ConfigureAwait(false).GetAwaiter().GetResult()
   // sync code
};

      

Here we have the sync code that needs to have access to the async api, so it blocks until the results are ready. We cannot change the signature and add async

here. So we're still waiting synchronously, so what do we need ConfigureAwait(false)

here? I'm sure we don't, but I'm a bit scared to remove it because it probably covers some use cases (or why do I see it pretty much everywhere? Is it just a piece of cargo?) And removing this call might lead to some unsafe results.

Does it make sense at all?

+3


source to share


1 answer


How to properly block asynchronous code?

You are not blocking asynchronous code correctly . The blocking is incorrect. Asking the question of what is right to do wrong is not a starter.

Blocking asynchronous code is wrong because of the following scenario:

  • I have an object in hand that represents an async operation.
  • The asynchronous operation itself asynchronously waits for the second asynchronous operation to complete.
  • A second asynchronous operation will be scheduled for this thread when the message loop executes the code associated with the message that is currently in this thread's message queue.

And now you can figure out what's going terribly wrong when you try to get the result in sync with the first asynchronous operation. It blocks until the async child completes, which will never happen because we have now blocked the thread that will serve the request in the future!

Your options:

  • Make the entire call stack correctly asynchronous and await

    result.
  • Don't use this API. Write an equivalent synchronous API that you know won't slow down from scratch and name it correctly.
  • Write the wrong program that sometimes deadlocks are unpredictable.

There are two ways to write a correct program; writing a synchronous wrapper over an asynchronous function is dangerous and wrong.



Now, you may be asking if the issue has been resolved with the ConfigureAwait

claim we are renewing in the current context? This is not the resume point we are worried about . If you want to rely on ConfigureAwait

to save you from deadlock, then every asynchronous operation on the stack must use it, and we don't know if this asynchronous operation is about to deadlock!

If the above isn't entirely clear to you, read Steven's article on why this is 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 suggesting more hacks and workarounds:

https://msdn.microsoft.com/en-us/magazine/mt238404.aspx?f=255&MSPPError=-2147217396

But then again: the right thing to do is to rewrite your program to embrace asynchrony and use it await

everywhere. Don't try to get around this.

because this method has ~ 20 methods stacktrace, some of them implement some interfaces. Changing async requires declaring changes in ~ 50 files, and we are fully converting the sync interfaces to mixed ones.

Get to work! This sounds pretty easy.

+11


source







All Articles