采用“异步”(即使它应该完成)作为MVC路线的一部分锁死路线; 这可怎么避免?(Using “a

2019-07-04 06:30发布

考虑以下(基于默认模板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;
}

不过,现在如果我们增加了一些async混进去:

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

    return result;
}

并更改到路由的第一行:

var task = IndirectSlowDouble(10);

那么它工作; 超时代替。 如果加上断点,在return result;async 的路径已经完成方法只发生-基本上,它看起来像系统是不愿使用任何线程恢复async操作,直到请求完成后。 更糟的是:如果我们使用.Wait()或访问.Result ),那么它就会完全死锁。

所以:什么是呢? 最明显的解决方法是“不涉及async ”,但消耗库等。当最终是不容易的,之间并不存在功能上的差异SlowDoubleIndirectSlowDouble (虽然有明显的结构性差异)。

注:在控制台/ WinForm的同样的事情在/ etc将正常工作。

Answer 1:

这是与同步上下文在ASP.NET(预.NET 4.5)实现的方式来做。 有吨的有关此问题的问题:

Task.WaitAll挂在ASP.NET多个awaitable任务

Asp.net的SynchronizationContext锁定的HttpApplication用于异步延续?

在ASP.NET 4.5,有一个新的实现,它的这款文章中描述的同步上下文。

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



Answer 2:

当您使用.Result总有因为死锁的可能性.Result被大自然阻塞。 避免死锁的方式是不是在任务阻塞(你应该使用asyncawait一路下跌)。 主题是在这里所描述的详细信息:

  • 不要阻塞异步代码

一个解决办法是增加ConfigureAwait

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

    return result;
}


Answer 3:

另一个解决办法是使用async / await遍布:

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


文章来源: Using “async” (even if it should complete) as part of a MVC route deadlocks the route; how can this be avoided?