为什么我不能同步运行异步代码



我正在尝试理解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");
        }
    }
}

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

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

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

我有一个博客条目,详细介绍了全部细节。

这是一个

死锁。在同步方法中,您正在等待自己:

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

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

相关内容

  • 没有找到相关文章

最新更新