我找到了这个方法:
public void LogoutAllSessions()
{
Task.Run(async () => await _identityHttpClient.GetAsync(Uri)).Wait();
// Could this be rewritten as this:?
// _identityHttpClient.GetAsync(Uri).GetAwaiter().GetResult();
}
这是否只是在另一个任务中运行一个任务并在等待外部任务完成时阻塞线程?
有人告诉我它是这样写的,因为将其更新为仅使用普通的 async/await 会涉及更改堆栈上的太多方法。
_identityHttpClient.GetAsync(Uri).GetAwaiter().GetResult();
会做与Task.Run(async () => await _identityHttpClient.GetAsync(Uri)).Wait();
相同的事情吗?
一个会比另一个更快吗?
如果您确实更改了
LogoutAllSessions
异步,您可能必须将其上面的调用函数设为异步,以便您可以等待它。要打破这个循环,您必须弄清楚异步堆栈何时会触发并被遗忘。然而,当前的 LogoutAllSessions
使用 Task.Run,无论如何,它都会在线程池中的单独线程中执行所有工作,因此实际上它已经作为单独线程的一部分完成了工作,就像您所做的那样未等待的异步函数中的工作。 .Wait() 冻结当前线程以等待所有注销所有会话发生。通常冻结整个线程并不是一个好主意。
至于
_identityHttpClient.GetAsync(Uri).GetAwaiter().GetResult()
和Task.Run(async () => await _identityHttpClient.GetAsync(Uri)).Wait()
,实际上是一样的。 Task.Run 生成新线程与 GetAsync(Uri).GetAwaiter() 以及异常处理之间存在一些差异,但两者都会在等待时锁定当前线程。
GetAwaiter
返回 TaskAwaiter
,它仅供编译器使用(此处的文档),所以我不能推荐它。既然这个任务再也不会被提及,为什么要等待呢?它可能会被重写为即发即忘的异步,因为该任务似乎不会以任何方式、形状或形式进行处理。
会做与_identityHttpClient.GetAsync(Uri).GetAwaiter().GetResult();
相同的事情吗?Task.Run(async () => await _identityHttpClient.GetAsync(Uri)).Wait();
这要看情况。在存在与特定线程绑定的同步上下文(如 UI 上下文)的情况下,或者一次允许一个线程进入(如“经典”ASP.NET 线程),具体取决于
_identityHttpClient
实现,第一个可能会导致死锁。
有关该主题的更多信息 - 查看 Stephen Cleary 的 不要阻止异步代码
一个会比另一个更快吗?在这种情况下我认为并不那么重要,第二个会更慢(卸载到线程池),但通常并不那么重要。
GetAsync
包装在
Task.Run
中,然后再与
.Wait()
或
.GetAwaiter().GetResult()
同步等待。原因是为了防止死锁,如果
GetAsync
/
async
实现,当前线程上安装了
await
(典型的 GUI 应用程序),在
SynchronizationContext
内部有一个
await
GetAsync
未配置
.ConfigureAwait(false)
,并且可等待 (
Task
) 尚未在
await
点完成。在这些情况下,死锁会持续发生(没有
Task.Run
),您将在测试期间立即观察到它(应用程序将挂起),并且您将被迫修复它。用
Task.Run
包装调用是修复此问题的一种方法,因为
Task.Run
会调用
ThreadPool
上的异步方法,而未安装
SynchronizationContext
。如果删除
Task.Run
并且您的应用程序没有挂起,则很可能
Task.Run
是多余的。冗余的
Task.Run
对性能的影响可以忽略不计。它可能是有益的,也可能是有害的,但差异很小。要了解死锁的原因,您可以看一下这个问题:
导致死锁的 async/await 示例。