使用"async"(即使它应该完成)作为MVC路由的一部分,会使路由死锁; 怎么能避免这个?

Mar*_*ell 19 .net asp.net asp.net-mvc asynchronous task-parallel-library

考虑以下(基于默认的MVC模板),这是在后台发生的一些"东西"的简化版本 - 它完成得很好,并显示预期的结果,20:

public ActionResult Index()
{
    var task = SlowDouble(10);
    string result;
    if (task.Wait(2000))
    {
        result = task.Result.ToString();
    }
    else
    {
        result = "timeout";
    }

    ViewBag.Message = result;
    return View();
}
internal static Task<long> SlowDouble(long val)
{
    TaskCompletionSource<long> result = new TaskCompletionSource<long>();
    ThreadPool.QueueUserWorkItem(delegate
    {
        Thread.Sleep(50);
        result.SetResult(val * 2);
    });
    return result.Task;
}
Run Code Online (Sandbox Code Playgroud)

但是,现在如果我们async在混合中添加一些:

public static async Task<long> IndirectSlowDouble(long val)
{
    long result = await SlowDouble(val);

    return result;
}
Run Code Online (Sandbox Code Playgroud)

并将路线中的第一行更改为:

var task = IndirectSlowDouble(10);
Run Code Online (Sandbox Code Playgroud)

然后它不起作用 ; 它反而超时了.如果我们添加断点,方法return result;中的async方法只路由完成才会发生- 基本上,看起来系统async在请求完成之前不愿意使用任何线程来恢复操作.更糟糕的是:如果我们使用过.Wait()(或访问过.Result),那么它将完全陷入僵局.

那么:那是什么?最明显的解决方法是"不涉及async",但消耗库等.当最终是不容易的,没有功能差异之间SlowDoubleIndirectSlowDouble(虽然有明显的结构性差异).

注意:在console / winform/etc中完全相同的东西都可以正常工作.

dav*_*owl 11

这与在ASP.NET(Pre .NET 4.5)中实现同步上下文的方式有关.关于这种行为有很多问题:

Task.WaitAll挂起了ASP.NET中的多个等待任务

Asp.net SynchronizationContext锁定HttpApplication以实现异步延续?

在ASP.NET 4.5中,有一个本文中描述的同步上下文的新实现.

http://blogs.msdn.com/b/webdev/archive/2012/11/19/all-about-httpruntime-targetframework.aspx


tpe*_*zek 8

当你使用时.Result,总会有死锁的可能性,因为.Result它本质上是阻塞的.避免死锁的方法是不阻塞任务(你应该使用asyncawait一直向下).这里的主题详细描述如下:

一个修复是添加ConfigureAwait:

public static async Task<long> IndirectSlowDouble(long val)
{
    long result = await SlowDouble(val).ConfigureAwait(false);

    return result;
}
Run Code Online (Sandbox Code Playgroud)


Mar*_*ell 6

另一个解决方法是使用async/ await贯穿:

public async Task<ActionResult> Index()
{
    var task = IndirectSlowDouble(10);
    long result = await task;
    ViewBag.Message = result.ToString();
    return View();
}
Run Code Online (Sandbox Code Playgroud)