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

Eri*_*ick 2 c# async-await asp.net-mvc-5 asp.net-identity

我试图了解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");
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

Ste*_*ary 6

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

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

Hello2().Result工作的原因是它实际上不是一种async方法; 它只是在一个线程池线程上坚持一些工作,它将独立于请求线程完成.

我有一个博客文章,详细介绍.