2
votes

EDIT: I have now solved the issue by simply marking the button click handler as async void and awaiting the task. I thought WebForms couldn't deal with async in any way except when used with RegisterAsyncTask. While this solves my problem, I am still interested in why the code below deadlocks since it goes against my current understanding of how asynchronous C# code works, so answers are still appreciated.

I have a service exposing an async method that sends a request to some api. This method is consumed in a webforms codebehind. I understand that ASP.NET only allows one thread to execute at a time, so calling Task.Wait() causes a deadlock since the awaited task can't resume execution when completed since the context thread is blocked.

It is however my understanding (from reading this blog) that calling ConfigureAwait(false) on the awaited task causes the task to run on a thread pool thread instead, and therefore execution on the context thread can resume. I still get a deadlock from the code below though. Why is this?

protected void Activate(object sender, CommandEventArgs e)
{
    var someID = int.Parse((string) e.CommandArgument);
    DoAsyncThingWithID(someID).Wait();
}

private async Task DoAsyncThingWithID(int ID)
{
    try
    {
        await new SomeService()
            .DoSomeAsyncWork(ID)
            .ConfigureAwait(false);
    }
    catch (AppropriateException e)
    {
        DealWithIt();
    }
}

Might be of note: DoSomeAsyncWork(int) has more async methods "under" it. Right at the bottom there is an api wrapping object (responsible for sending the HTTP request) with a method that is NOT async, but which is invoked with Task.Run(() => api.SendThingy());

Could this be the issue?

2
Post your finding as an answer and accept it.Lex Li

2 Answers

5
votes

It is however my understanding (from reading this blog) that calling ConfigureAwait(false) on the awaited task causes the task to run on a thread pool thread instead, and therefore execution on the context thread can resume.

Actually, using ConfigureAwait(false) means that the continuation of the current method doesn't care what context it executes in - and the vast majority of the time this means it will continue on a thread pool thread.

So, DoSomeAsyncWork will still run on the ASP.NET context. For this reason, just a single ConfigureAwait(false) is insufficient. You'll have to ensure that DoSomeAsyncWork also uses ConfigureAwait(false), along with all async methods it calls, and all async methods they call, etc., including Microsoft or third-party library methods.

This is why I recommend not blocking to begin with. The ConfigureAwait(false) hack is just one of the ways to try to get around it if you absolutely have to.

1
votes

I have now solved the issue by simply marking the button click handler as async void and awaiting the task. I thought WebForms couldn't deal with async in any way except when used with RegisterAsyncTask. While this solves my problem, I am still interested in why the original code deadlocks since it goes against my current understanding of how asynchronous C# code works, so further answers are still appreciated.