何时使用Task.Run().GetAwaiter().GetResult()和().GetAwaiter.GetResult()?

14

我有一个async任务需要同步调用(是的,不幸的是,这是不可避免的)。似乎有两种实现方式 - 每种都似乎可以工作。因此,我不确定哪种方法最好,或者是否存在更好的方法。

例如:

var meetings = Task.Run(() => GetTodaysMeetingsAsync()).GetAwaiter().GetResult();

var meetings = GetTodaysMeetingsAsync().GetAwaiter().GetResult();

如果有人能解释为什么一种方法比另一种更好,那将不胜感激。谢谢!


2
@FrankNielsen 这只适用于已经摆脱了 SynchronizationContext 的 ASP.NET Core。 - Tanveer Badar
2
@FrankNielsen 这仅适用于ASP.NET Core,而不适用于整个.NET Core。 - GSerg
2
@FrankNielsen 这完全是错误的。.NET Core不仅仅是ASP.NET Core,后者是前者的一部分,不要混淆两者。随着WPF和Windows Forms在3.0中加入.NET Core,如果你在.NET Core上,建议任何人不关心死锁问题甚至更加有问题。 - Tanveer Badar
1
这对于ASP.NET和控制台是正确的,除非有人决定在运行程序中安装非默认的“SynchronizationContext”。 - Tanveer Badar
代码只需在 .Net 3 的控制台应用程序上运行,所以我假设 @FrankNielsen 的建议可以忽略? - Adam
显示剩余8条评论
1个回答

20
当你使用 Task.Run 时,委托的初始同步部分将在线程池线程上运行,而只有 ().GetAwaiter().GetResult() 将在同一线程上运行该同步部分。
使用 Task.Run(...).GetAwaiter().GetResult() 可以作为运行异步代码并同步等待它的解决方法,它不会导致异步死锁,而 ().GetAwaiter().GetResult() 可能会。请注意,它仍然不是“安全”的,在其中您可能正在阻塞线程池线程,这可能会导致服务器负载下的线程池耗尽。
如果要运行返回 Task 的方法,并且知道初始同步部分是微不足道的,并且您知道异步方法的其余部分不会使用 SynchronizationContext 运行,那么只使用 ().GetAwaiter().GetResult() 可能是一种微优化。我认为只有在确切知道自己在做什么的情况下才应这样做。
如何确定您是否没有运行在 SynchronizationContext 下?由于以下原因之一,SynchronizationContext.Current 将为 null
  • 知道自己的代码正在不需要该对象的应用程序模型下运行(例如控制台应用、ASP.NET Core、Windows 服务)
  • 在当前堆栈中先前使用了 .ConfigureAwait(false) 来等待不完整的 Task
  • 你已经明确调用了 SynchronizationContext.SetSynchronizationContext(null)
  • 因此,你可以看到需要考虑的东西很多,通常情况下你几乎总是想要使用 Task.Run(...).GetAwaiter.GetResult()


    1
    如果您想了解是否在SynchronizationContext下运行,您还可以查询属性SynchronizationContext.Current - Theodor Zoulias
    我正在使用Net7,并且已经重写了PageModel,用自己的页面来获取内容。问题是我不能在重写的页面上使用OnGet,而必须使用OnPageHandlerExecuted。然后我必须使用.GetAwater().GetResult(),因为我的Razor页面在API调用完成之前就已经渲染了。所以任何模型更新都不完整,导致出现空异常。有什么建议吗? - undefined

    网页内容由stack overflow 提供, 点击上面的
    可以查看英文原文,
    原文链接