Bradley Uffner
Bradley Uffner

Reputation: 17001

Contradictory advice regarding async / await in MVC

I've been developing some library code that will be consumed by MVC action methods, so I've been reading a lot of Steven Cleary's blogs about the subject.

I ran in to his "Don't Block on Async Code" blog post, but am confused about some parts that seem contradictory.

Under "Preventing the Deadlock" he states:

There are two best practices (both covered in my intro post) that avoid this situation:

  1. In your “library” async methods, use ConfigureAwait(false) wherever possible.

  2. Don’t block on Tasks; use async all the way down.

Then later:

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).

Given these 2 statements, one saying that ConfigureAwait(false) is a best practice for library code, and one that says it is at best a dangerous hack, what is the actual proper way to write library code that won't deadlock when used in MVC?

I've read other question and answers on SO that might be considered duplicates of this, but none of them seem to address the contradictory information given by one of the experts on async / await.

Upvotes: 1

Views: 113

Answers (1)

Paul Turner
Paul Turner

Reputation: 39685

The information isn't directly contradictory unless you take them out of context:

The former is general advice to writing library code; it's a good idea to do these things to make sure your library behaves well regardless of the synchronization context it's called in.

The latter is explicitly calling out how using ConfigureAwait(false) as a means to avoid a known deadlock scenario is a hack. It's saying,

I want to block here, but doing so would normally cause a deadlock, so I'll push the work to another thread (from the thread-pool) and hope everything works out okay.

If following the earlier advice to not block on tasks (use async all the way down), then this deadlock scenario won't happen.

The deadlock scenario is only relevant when running code within specific synchronization contexts which schedule continuations to execute on the thread which started the task, such as the one used by ASP.NET (a request is processed on one thread) or the one used by Windows desktop applications (a single thread for all UI events).

Blocking on the task in these contexts causes the single thread to block, so the thread is never free to run the continuation and the method deadlocks.

Upvotes: 3

Related Questions