每当使用异步时,ASP.Net MVC 4 控制器就会挂起

Posted

技术标签:

【中文标题】每当使用异步时,ASP.Net MVC 4 控制器就会挂起【英文标题】:ASP.Net MVC 4 controller hangs whenever async is used 【发布时间】:2012-07-06 14:34:37 【问题描述】:

我正在使用带有 .Net 4.5 和 ASP MVC 4 RC 的 Visual Studio 2012 RC。每当我使用异步时,它都会挂起。控制器操作方法使用异步,但本身不是异步控制器方法。

没有记录错误或抛出异常,但浏览器永远显示“Waiting for www.myweb.local”。

// Simplest possible async
public class Waiter

    public async Task<int> GetValue()
    
        await Task.Yield();
        return await Task.Factory.StartNew(() => 42);
    


// simplest possible controller that uses the async
public class HomeController : Controller

    public ActionResult Index()
    
        var waiter = new Waiter();
        var resultTask = waiter.GetValue();
        int result = resultTask.Result;

        // it never gets here 
        return View();
    

我已经完成了in this answer 中提到的事情,但它仍然不起作用。 IE。 web.config 包含

<add key="aspnet:UseTaskFriendlySynchronizationContext" value="true"/>

而神奇的词await Task.Yield();在异步方法中。

.Net 框架版本是 4.5.50501。我在 IIS Express 和 IIS 6.0 上观察到了这种行为。

我尝试将“2012 年 7 月更新”应用到 VS2012,但这并没有解决问题。

This answer suggests 这可能是因为当我等待它时任务已经完成,但是如果是这样的话,这应该有效,但它没有:

public class Waiter

    public async Task<int> GetValue()
    
        await Task.Yield();
        return await Task.Factory.StartNew(() => 
            
                Thread.Sleep(1500);
                return 42;
            );
    

有几个人建议需要ConfigureAwait(false),但此代码也不起作用:

    public async Task<int> GetValue()
    
        var task = new Task<int>(() => 42);
        return await task.ConfigureAwait(false);
    

以下确实适用于剃刀视图引擎,但不适用于 spark。当然应该有办法让其他场景也能正常工作?不能在同步代码中使用异步任务吗?

public class Waiter

    public async Task<int> GetValue()
    
        return await Task.Factory.StartNew(() => 42);
    


public class HomeController : Controller

    public async Task<ActionResult> IndexAsync()
    
        await Task.Yield();
        var waiter = new Waiter();
        int result = await waiter.GetValue();

        return View();
    

我知道这是不是发布的软件,但微软的 RC 通常很稳定,所以我很惊讶它失败了,而且失败的方式毫无帮助。

【问题讨论】:

对不起,如果我遗漏了什么,但为什么通过访问 Result 属性而不是将其标记为异步并在那里使用 await 来阻止索引?如果这是有意为之,您可能需要使用 ConfigureAwait(false) 以便继续不会安排回同一个线程。 我正在测试将现有网站移植到 .Net 4.5。它使用火花视图引擎。我想在处理异步控制器之前让异步工作,以及 spark 是否可以处理 Task (我最初的印象是没有)。但不管我能不能做到这一点,这种情况应该可行。 为什么要从 AsyncController 派生?那没有必要。查看我的异步示例asp.net/mvc/tutorials/mvc-4/… 感谢 Rick 的提示,我已经修改了代码,例如适合。 【参考方案1】:

你正在造成死锁,just like this question。

James Manning 的建议是正确的,但你必须await ConfigureAwait 的结果,像这样:

public async Task<int> GetValue()

    var task = new Task<int> (() => 42);
    return await task.ConfigureAwait(false);

一般来说,混合同步和异步代码是一个非常糟糕的主意,除非您真的知道自己在做什么。使控制器动作异步会好得多。

【讨论】:

我同意 - 特别是现在使控制器方法异步变得如此容易。虽然我可以看到一些(非常 非常 罕见)在同步处理程序中并行运行某些任务可能有意义的情况。在被异步控制器迷住之前,总是值得一读这篇文章:blog.stevensanderson.com/2010/01/25/… 肯定有任何使用 async 关键字“混合同步和异步代码”的 c# 程序吗?使用异步控制器只是意味着边界在框架中。 关于“现在让控制器方法异步变得如此容易” - 如果您有很多使用 spark 视图引擎的现有视图,则不会。 @Anthony:我所说的“混合”是指每个应用程序都有一个事件控制器(UI 应用程序有一个消息泵;ASP.NET 应用程序有一个请求上下文)。将async 一直延伸到该事件控制器是最简洁和最简单的设计。您不想在将同步事件处理程序调用到async 层的意义上“混合”。 我没用过spark,但如果只是一个视图引擎,你就不能让你的控制器方法async反正?【参考方案2】:

我在测试版中的异步工作很好。我没有测试过,但我猜这是因为你的控制器方法不是异步的。改成这样:

public async Task<ActionResult> IndexAsync()

    var waiter = new Waiter();
    int result = await waiter.GetValue();

    // it never gets here 
    return View();

【讨论】:

如果是“因为控制器方法不是异步的”,你是说使用异步任务永远不会在同步代码中工作吗?否则,应该有办法让这段代码正常工作。 那么 GetValue() 没有完成......我想不出一个明显的原因,从你所拥有的,我现在在火车上!如果没有人打败我,我稍后会看看。 如果你在等待之后访问结果(用它做某事)会发生什么?

以上是关于每当使用异步时,ASP.Net MVC 4 控制器就会挂起的主要内容,如果未能解决你的问题,请参考以下文章

ASP.NET MVC中使用异步控制器

ASP.NET MVC4 异步控制器 - 为啥要使用?

什么时候应该在 ASP.NET MVC 中使用异步控制器?

ASP.NET MVC 4 异步子动作

asp.net mvc中的单元测试问题?

使用 ASP.NET MVC 4 从控制器调用另一个不同的视图