Mar*_*ein 2 c# deadlock asynchronous async-await
我尝试理解之间的概念GetAwaiter().GetResult()。我知道如果可能的话不应该使用它,但在这种情况下我不能使用 async/await,所以我必须从同步事务中的异步函数获取结果。
让我们考虑这个简单的 Blazor 服务器应用程序:
@page "/"
<button @onclick="GetAwaiterGetResultOnTask">Task GetAwaiter().GetResult()</button>
<button @onclick="GetAwaiterGetResultOnFunction">Function GetAwaiter().GetResult()</button>
<p>@_message</p>
@code {
private string _message;
private async Task<string> AsyncFunction(string message)
{
await Task.Delay(500);
return message;
}
private void GetAwaiterGetResultOnTask()
{
_message = Task.Run(() => AsyncFunction("Message from GetAwaiterGetResultOnTask")).GetAwaiter().GetResult();
}
private void GetAwaiterGetResultOnFunction()
{
_message = AsyncFunction("Message from GetAwaiterGetResultOnFunction").GetAwaiter().GetResult();
}
}
Run Code Online (Sandbox Code Playgroud)
调用该函数GetAwaiterGetResultOnFunction将导致死锁。然而,当我打电话时却GetAwaiterGetResultOnTask没有。
它们与功能之间的主要区别是什么?为什么调用时不会导致死锁Task.Run?
await知道一个东西叫做SynchronizationContext。如果存在这样的上下文(SynchronizationContext.Current不为空),则将后续传递await给该上下文,因为它应该更好地知道如何处理它(除非您明确告诉不要这样做,使用await someTask.ConfigureAwait(continueOnCapturedContext: false))
大多数 UI 框架不喜欢从多个线程同时访问 UI,因为这通常会导致各种难以调试的问题。它们用于SynchronizationContext强制执行单一流程。为此,他们通常将发布到 SynchronizationContext 的回调放入某种队列中,并在单个“UI”线程上一一执行它们。
Blazor 也这样做。GetAwaiterGetResultOnFunction您的情况是在该“UI”线程上执行的,SynchronizationContext可用。当执行到达await Task.Delay内部时AsyncFunction- 当前上下文被捕获,并且值得注意的是,当Task.Delay完成时 - 函数的其余部分应该被发布到上下文以供执行。
GetResult然后,您可以通过执行从 . 返回的任务来阻止“UI”线程AsyncFunction。然后Task.Delay完成并将函数的其余部分发布到SynchronizationContextBlazor 来执行。但是,要做到这一点,Blazor 需要您刚刚阻止的相同 UI 线程,从而导致死锁。
当您调用时GetAwaiterGetResultOnTask-AsyncFunction不在 Blazor 的“UI”线程上运行 - 它在其他某个(线程池)线程上运行。SynchronizationContext为 null,之后的部分await Task.Delay将在某个线程池线程上运行,并且不需要“UI”线程来完成。那么就不存在僵局了。
| 归档时间: |
|
| 查看次数: |
1335 次 |
| 最近记录: |