Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/ant/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Asp.net 使用;异步;(即使应完成)作为MVC路线的一部分,该路线会死锁;如何避免这种情况?_Asp.net_.net_Asp.net Mvc_Asynchronous_Task Parallel Library - Fatal编程技术网

Asp.net 使用;异步;(即使应完成)作为MVC路线的一部分,该路线会死锁;如何避免这种情况?

Asp.net 使用;异步;(即使应完成)作为MVC路线的一部分,该路线会死锁;如何避免这种情况?,asp.net,.net,asp.net-mvc,asynchronous,task-parallel-library,Asp.net,.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 =

考虑以下内容(基于默认的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;
}
并将路线中的第一行更改为:

var task = IndirectSlowDouble(10);
然后它就不起作用了;而是超时了。如果我们添加断点,
返回结果async
方法中,code>仅在路由完成后发生-基本上,在请求完成之前,系统不愿意使用任何线程来恢复
async
操作。更糟糕的是:如果我们使用了
.Wait()
(或者访问了
.Result
),那么它将完全死锁

那么:这是怎么回事?显而易见的解决办法是“不涉及
async
”,但这在使用库等时并不容易。最终,
SlowDouble
IndirectSlowDouble
之间没有功能上的区别(尽管存在明显的结构差异)


注意:在控制台/winform/etc中使用完全相同的方法可以正常工作。

当您使用
.Result
时,始终存在死锁的可能性,因为
.Result
本质上是阻塞的。避免死锁的方法是不要阻塞任务(您应该一直使用
async
wait
)。此处详细描述了该主题:

一种修复方法是添加
ConfigureAwait

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

    return result;
}
publicstaticasync任务IndirectSlowDouble(long val)
{
long result=wait SlowDouble(val)。configurewait(false);
返回结果;
}

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

在ASP.NET4.5中,本文介绍了同步上下文的一个新实现


另一个修复方法是在整个过程中使用
async
/
wait

public async Task<ActionResult> Index()
{
    var task = IndirectSlowDouble(10);
    long result = await task;
    ViewBag.Message = result.ToString();
    return View();
}
公共异步任务索引()
{
var任务=间接慢倍率(10);
长期结果=等待任务;
ViewBag.Message=result.ToString();
返回视图();
}

阻塞不是死锁;问题中显示的代码正确地使用了带超时的等待。最终,这一切都可以完成——请看问题的最后一行:在MVC之外,这是绝对好的。即使如此:只有添加了形式上异步的方法才打破了它。但是,是的,ASP.NET同步上下文似乎是其中的一部分。编辑后添加了
ConfigureAwait
,这样就解决了库代码方面的问题,库代码直接来自一个链接的pages@MarcGravell当然,阻塞不是死锁,但阻塞行为可能导致死锁。这里的原因似乎是特定的“上下文”(在ASP.NET的情况下是请求上下文),同样的事情很可能在WebForms中不起作用,而不仅仅是ASP.NET MVC。如果你确定不是这样,我将删除答案。不,这是一个好答案-请不要删除它。同步上下文似乎是这里的罪魁祸首。
await SlowDouble(val)。在
IndirectSlowDouble
方法中,ConfigureAwait(false)
可以防止死锁吗?添加交叉引用链接:实际上,在WinForms中也会发生相同的超时。这不会发生在控制台应用程序中。我写了这篇文章来解释这些差异。@StephenCleary我被纠正了-我的测试中一定有什么不可靠的地方,这意味着它没有一个同步上下文新的同步上下文看起来很有希望是“ASP.NET 4.5”-是不是当前运行在.NET 4.5上的ASP.NET?或者这是一个悬而未决的释放?我的目标是4.5等,并尝试显式设置所有这些标志,但它不会改变行为,因为我可以告诉它的默认情况下,如果你的目标是.NET4.5,但它不会使挂起消失。正如文章所说,“重要的是,async/await的行为在ASP.NET中是未定义的,除非设置了此开关。”。您仍然需要使用Wait everywhere以避免死锁。因此命名有点混乱。。。与其说是“…任务友好…”,不如说是“…任务稍微不安全…”;p感谢您的澄清。4.5版本之前的ASP.NET
SynchronizationContext
一次只允许一个线程进入。好吧,.NET4.5ASP.NET
SynchronizationContext
也有同样的限制,因此新版本不会修复超时。我认为最好的解决方案是使您的操作
异步
,并使用基于
CancellationToken
的超时。是的,这是我更喜欢的选项,但我仍然会在不需要同步上下文的库级别使用
ConfigureWait
。特别是,当您将库分发给其他人使用时。@tugberk返回一个
ConfiguredTaskAwaitable[]
并不是大多数调用者所期望的-它与
Task[]
相比是非常不清楚的。我认为这是订阅者必须应用的东西。另外:在不知道调用者的情况下,不清楚它是否应该在上下文中等待。但是如果你在你的库中的异步方法中等待一个异步方法,你的消费者会以阻塞的方式使用它,并且有一个可用的同步上下文,你仍然会以死锁告终。OTOH,在应用程序级别,你几乎总是不想使用
ConfigureAwait
。我在上面链接的博客文章试图根据Stephan Toub的//build 2011 async talk解释这一点。@tugberk哦,当然,库中的任何
wait
代码都应该这样做。。。在我的例子中,我所说的库公开了
任务
/
任务
,但实际上不做任何
内部等待
(或任何其他类型的巫毒-任务实际上是基于
任务完成源代码
)是的,除非您能够
public async Task<ActionResult> Index()
{
    var task = IndirectSlowDouble(10);
    long result = await task;
    ViewBag.Message = result.ToString();
    return View();
}