0

This question is different from await vs Task.Wait - Deadlock?. That question deals with a case where (purportedly) await causes a deadlock, and .Wait doesn't. This question is the reverse. Additionally, there is disagreement between the question and answerer on the other question, so it cannot answer my question at all.

This causes a deadlock in the context of an ASP.Net request:

    protected void TestBtnClick(object sender, EventArgs e)
    {
        DoSomethingAsync()
            .Wait();
    }


    private async Task DoSomethingAsync()
    {
        await Task.Delay(2000)
                  .ConfigureAwait(continueOnCapturedContext: true);
    }

My understanding is that the first calling method is waiting to marshall the original synchronisation context, and then the called method also waits to marshall the original context, thus causing the deadlock.

If this is the case, why doesn't the following also cause a deadlock?

    protected async void TestBtnClickAsync(object sender, EventArgs e)
    {
        await DoSomethingAsync()
            // ****** DIFFERENCE IS HERE: ******
            .ConfigureAwait(continueOnCapturedContext: true);
    }


    private async Task DoSomethingAsync()
    {
        await Task.Delay(2000)
                  .ConfigureAwait(continueOnCapturedContext: true);
    }

Note that both async calls explicitly request to continue on the original synchronisation context.

Community
  • 1
  • 1
Jordan Morris
  • 2,101
  • 2
  • 24
  • 41

2 Answers2

2

Because in your first code block, you're explicily calling Wait(). This blocks the thread's execution so that the continuation can never be scheduled on that thread. The thread never returns to the pool, it sits there waiting for a signal to continue executing, which it can never receive because that signal would come form the continuation that is waiting to be scheduled on the waiting thread.

In your second block, the await keyword yields the thread back to the pool so that later on the continuation can be scheduled on that thread again when it becomes free (from handling other requests).

CodingGorilla
  • 19,612
  • 4
  • 45
  • 65
1

The reason you can have lots of awaits resuming on the same context is because they let others use it in the meantime.

Imagine that executing on the thread is like the stick in one of those "have to hold the stick to talk" groups. Your first example is like one person asking someone a question, then refusing to give up the stick until the question is answered. But the other person can't answer until they get the stick! Clearly there's going to be a problem here.

In your second example, the person asks someone a question, asks to be given the stick back after the answer is given, then gives up the stick. There might be some unrelated chatter along the way, but at least the question is going to get answered. Much better.

(Note that continuing on the captured context is the default, so you're not changing anything by explicitly asking for it.)

Craig Gidney
  • 17,763
  • 5
  • 68
  • 136