简体   繁体   中英

Blazor server-side async deadlock

I have an async method that I want to call from blazor compoment. I need to call it directly from component, not from its lifecycle hooks (where it works well).

I am not sure why some of my cases work and some don't.

I know that synchronously waiting for an async Task inside ASP/UI context thread may casue deadlock. But here, the situation is little different since ConfigureAwait(false) doesn't help.

My assumption is that it has somethig to do with fact, that in Blazor server-side there are multiple possible UI threads, so when I set ConfigureAwait(false) then in most cases the rest of work will be processd by different, but still UI thread.

Can someone explain what's happening there?

Here is my component's code (simplified):

@page "/fetchdata"
@inject AsyncTaskService TaskService

//works
<p>Google request sync result: @TaskService.RequestGoogle()</p>

//deadlock
<p>Google request async result #1: @TaskService.RequestGoogleAsync().Result</p>

//deadlock
<p>Google request async result #2: @TaskService.RequestGoogleAsync().ConfigureAwait(false).GetAwaiter().GetResult()</p>

//works (because ThreadPoolThread performs requesting?)
<p>Google request async result #3: @(Task.Run(() => TaskService.RequestGoogleAsync()).Result)</p>

//can not compile
<p>Google request async result #4: @await TaskService.RequestGoogleAsync()</p>

AsyncTaskService:

public class AsyncTaskService
{
    private readonly HttpClient _httpClient;

    public AsyncTaskService(HttpClient httpClient)
    {
        this._httpClient = httpClient;
    }

    public string RequestGoogle()
    {
        var webResponse = _httpClient.GetAsync("https://www.google.com").Result;
        var result = webResponse.StatusCode.ToString();

        return result;
    }

    public async Task<string> RequestGoogleAsync()
    {
        var webResponse = await _httpClient.GetAsync("https://www.google.com");
        var result = webResponse.StatusCode.ToString();

        return result;
    }
}

I know that synchronously waiting for an async Task inside ASP/UI context thread may casue deadlock. But here, the situation is little different since ConfigureAwait(false) doesn't help.

It's not different, actually. It's the exact same deadlock .

The problem with using ConfigureAwait(false) as a blocking hack is that it needs to be applied to every await within the transitive closure of all methods called from the point of the blocking. So for the blocking code TaskService.RequestGoogleAsync().GetAwaiter().GetResult() , that means that TaskService.RequestGoogleAsync has to use ConfigureAwait(false) on every await , and every method that TaskService.RequestGoogleAsync calls has to use ConfigureAwait(false) on every await , and every method that those methods call have to use ConfigureAwait(false) on every await , all the way down . Including code you don't control (library/framework code). And if you (or the library/framework code) miss even a single one, then you have a potential deadlock.

So, to walk through:

//deadlock
<p>Google request async result #2: @TaskService.RequestGoogleAsync().ConfigureAwait(false).GetAwaiter().GetResult()</p>

^ The ConfigureAwait(false) here does exactly nothing because there's no await being configured.

//works (because ThreadPoolThread performs requesting?)
<p>Google request async result #3: @(Task.Run(() => TaskService.RequestGoogleAsync()).Result)</p>

^ This one works because the thread pool doesn't have a "context" to capture, so the deadlock is avoided.

//can not compile
<p>Google request async result #4: @await TaskService.RequestGoogleAsync()</p>

^ This one would be ideal. I recommend that you do not block on asynchronous code (or on I/O).

If you cannot use await here, then you should use await before this point. I'm not that familiar with Blazor but with MVC it was common to use await when determining the model, which was then passed to the (synchronous) view. It sounds to me that lifecycle hooks may be the better solution.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM