繁体   English   中英

Blazor 服务器端异步死锁

[英]Blazor server-side async deadlock

我有一个我想从 blazor 组件调用的异步方法。 我需要直接从组件调用它,而不是从它的生命周期钩子(它工作得很好)。

我不确定为什么我的一些案例有效而有些无效。

我知道在ASP/UI context线程中同步等待异步任务可能会导致死锁。 但是在这里,情况略有不同,因为ConfigureAwait(false)没有帮助。

我的假设是它与事实有关,在Blazor server-side有多个可能的 UI 线程,所以当我设置ConfigureAwait(false)时,在大多数情况下 rest 的工作将由不同的处理,但仍然是UI线。

有人可以解释那里发生了什么吗?

这是我的组件的代码(简化):

@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>

异步任务服务:

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;
    }
}

我知道在 ASP/UI 上下文线程中同步等待异步任务可能会导致死锁。 但是在这里,情况略有不同,因为 ConfigureAwait(false) 没有帮助。

实际上,这并没有什么不同。 这是完全相同的死锁

使用ConfigureAwait(false)作为阻塞 hack的问题在于,它需要应用于从阻塞点调用的所有方法的传递闭包内的每个await 因此,对于阻塞代码TaskService.RequestGoogleAsync().GetAwaiter().GetResult() ,这意味着TaskService.RequestGoogleAsync必须在每个await上使用ConfigureAwait(false)并且TaskService.RequestGoogleAsync调用的每个方法都必须使用ConfigureAwait(false)在每个await上,并且这些方法调用的每个方法都必须在每个await上使用ConfigureAwait(false)一直到下去 包括您无法控制的代码(库/框架代码)。 如果您(或库/框架代码)甚至错过了一个,那么您就有潜在的死锁。

因此,要遍历:

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

^ 此处的ConfigureAwait(false)完全没有任何作用,因为没有配置await

//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>

^ 这将是理想的。 我建议您不要阻塞异步代码(或 I/O)。

如果您不能在此处使用await ,那么您应该在此之前使用await 我对 Blazor 不太熟悉,但是对于 MVC,在确定 model 时通常使用await ,然后将其传递给(同步)视图。 在我看来,生命周期挂钩可能是更好的解决方案。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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