为啥我不能将异步代码作为同步运行 [重复]

Posted

技术标签:

【中文标题】为啥我不能将异步代码作为同步运行 [重复]【英文标题】:Why can't I run async code as synchronous [duplicate]为什么我不能将异步代码作为同步运行 [重复] 【发布时间】:2013-12-16 21:51:19 【问题描述】:

我正在尝试了解 MVC 的异步/等待机制。现在我不考虑退出“正常流程”(使用完全同步或完全异步端到端)的用例。我只是想确保了解它为什么在这里不起作用。

当调用“SyncMethod”时,它会无限期挂起并且永远不会返回。

当“AsyncMethod”被调用时,它会快速返回一个视图而不会挂起。

当“TaskMethod”被调用时,它会快速返回一个视图而不会挂起。

我不确定为什么当同步方法调用异步方法时不可能返回结果。

我在这里错过了什么?

using System;
using System.Threading.Tasks;
using System.Web.Mvc;

namespace MvcAsyncAwaitTest.Controllers

    public class HomeController : Controller
    
        /// <summary>
        /// Synchronous method running async method as sync
        /// Hangs at Hello().Result, never returns
        /// </summary>
        /// <returns></returns>
        public ActionResult SyncMethod()
        
            ViewBag.Message = Hello().Result;
            return View();
        

        /// <summary>
        /// Asynchronous method awaiting asynchronous method
        /// Do not hang 
        /// </summary>
        /// <returns></returns>
        public async Task<ActionResult> AsyncMethod()
        
            ViewBag.Message = await Hello();
            return View("Index");
        

        /// <summary>
        /// Synchronous method running a task based method synchronously
        /// Returns a valid result
        /// </summary>
        /// <returns></returns>
        public ActionResult TaskMethod()
        
            ViewBag.Message = Hello2().Result;

            return View("index");
        

        private async Task<string> Hello()
        
            return await HelloImpl();
        

        private Task<string> Hello2()
        
            return Task.Run(() => "Hello world 2");
        

        private async Task<String> HelloImpl()
        
            return await Task.Run(() => "Hello World");
        
    

【问题讨论】:

Stephen Cleary 在这里解释了异步/死锁困境:blog.stephencleary.com/2012/07/dont-block-on-async-code.html Hello() 的意义何在?它什么也没做。直接拨打HelloImpl即可。此外,创建一个方法async 只是为了等待您返回的一项任务是没有意义的;只需使方法不是async 并直接返回该任务。 @Servy 我可以做一个复杂的实现,但我更喜欢举一个非常简单的例子来准确理解这里的问题。但是,如果您想要一个更复杂的实现,请查看使用方式完全相同的 asp.net 身份框架。 @Erick 我在描述如何简化代码,而不是告诉你让它变得更复杂...... 【参考方案1】:

问题的症结在于await 将(默认情况下)捕获当前“上下文”并使用它来恢复async 方法。在 ASP.NET 中,“上下文”是一个 SynchronizationContext,一次只允许一个线程进入。

因此,当您通过调用 Result 阻塞请求线程时,您正在阻塞该 SynchronizationContext 中的一个线程,因此 Hello 方法无法在该请求上下文中恢复。

您的Hello2().Result 起作用的原因是它实际上不是async 方法;它只是在线程池线程上做一些工作,这将独立于请求线程完成。

我有一个blog entry,其中包含详细信息。

【讨论】:

【参考方案2】:

这是一个僵局。在 SyncMethod 中,您正在等待自己:

Hello().Result 等待您的请求线程。

await HelloImpl()HelloImpl() 内部的await Task.Run(...) 都将执行返回到请求的线程,但不能因为.Result 阻塞它。

【讨论】:

以上是关于为啥我不能将异步代码作为同步运行 [重复]的主要内容,如果未能解决你的问题,请参考以下文章

同步和异步实现的代码重复

异步和同步方法[重复]

为啥 Vector<String> 在以多线程样式访问它时不以同步方式运行 [重复]

为啥 Vector<String> 在以多线程样式访问它时不以同步方式运行 [重复]

Spring boot:使用异步方法作为同步方法

为啥这段代码不能在 Java 中运行? [复制]