[英]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.