2

更新

经过一番思考,我得出结论,我可能没有问对正确的问题。

给定以下代码:

public class HomeController : Controller
{
    public Task<string> DownloadAsync(string url)
    {
        using (var web = new WebClient())
        {
            return web.DownloadStringTaskAsync(url);
        }
    }

    // THROWS EXCEPTION
    public ActionResult Index()
    {
        var data = DownloadAsync("http://google.dk");
        return Content(data.Result);
    }

    // WORKS
    public async Task<ActionResult> IndexWorks()
    {
        var data = await DownloadAsync("http://google.dk");
        return Content(data);
    }
}

很明显(尤其是在阅读了@Stephen Cleary 的博文之后),ActionResult Index()代码会导致死锁。但为什么?

经过一番挖掘,我发现它.NET 4.5引入了一个新AspNetSynchronizationContext的,应该更“任务友好”。下载源代码.NET 4.5并查看新的内部AspNetSynchronizationContext,我认为调用OperationStarted将导致对名为AllowVoidAsyncOperations. 如果该值为 true,则没有问题。但是,如果此布尔值的值为 false,它将引发以下异常:

此时无法启动异步操作。异步操作只能在异步处理程序或模块内或在页面生命周期中的某些事件期间启动。如果在执行页面时发生此异常,请确保将页面标记为 <%@ Page Async=\"true\" %>。

经过大量反思,我认为,调用 theasync Task<ActionResult> IndexWorks()以某种方式将 the 设置AllowVoidAsyncOperations为 true。- 调用同步版本时,它保持默认值:false。

因此,我的问题是:

什么时候异步 ActionResult 调用内部方法AspNetSynchrnoizationContext,设置AllowVoidAsyncOperations为 true?- 到目前为止,我已将其范围缩小到班级CallHandlerExecutionStep内的HttpApplication班级。- 但是,我不确定它如何决定是否允许它。

4

2 回答 2

4

如果您尝试在请求生命周期中不允许的点(或完全在请求上下文之外)执行异步操作,则 ASP.NET 会引发此特定异常。

您不应该在 ASP.NET MVC 中看到这一点。有两件事要检查:

  1. 确保您在 .NET 4.5 上运行。我怀疑您已经是,否则您根本不会看到该消息。
  2. 确保您已UseTaskFriendlySynchronizationContext设置为true httpRuntime.targetFramework设置为4.5

更新:经过反思,在 ASP.NET MVC 中还有另外两个可能导致此问题的条件:

  1. 确保您没有调用任何async void方法。
  2. 确保您没有使用 EAP 组件。例如:
    1. HttpClientTAP,所以它会工作。
    2. HttpWebRequestAPM,因此围绕它的 TAP 包装器将起作用。
    3. WebClientEAP,所以会导致这个错误。无论您使用DownloadStringAsync还是DownloadStringTaskAsync.
于 2013-06-09T20:16:01.887 回答
1

您找不到将 UseTaskFriendlySynchronizationContext 设置为 true 的代码,因为设置它的代码不是 .NET 框架的一部分。

操作同步上下文的代码由 c# 5.0 编译器在编译期间生成。C# 编译器创建一个具有奇怪名称的状态机结构<yourmethod>d__0(以避免与您的代码冲突)。这个结构中有一个名为“MoveNext”的方法。那是捕获同步上下文的地方。你的大部分异步代码也会被编译器移到那里。

于 2013-06-19T14:24:59.770 回答