C# 等待vs任务。等待-死锁?

C# 等待vs任务。等待-死锁?,c#,task-parallel-library,deadlock,async-await,C#,Task Parallel Library,Deadlock,Async Await,我不太明白Task.Wait和Wait之间的区别 我在ASP.NET WebAPI服务中具有类似于以下函数的功能: public class TestController : ApiController { public static async Task<string> Foo() { await Task.Delay(1).ConfigureAwait(false); return ""; } public asy

我不太明白
Task.Wait
Wait
之间的区别

我在ASP.NET WebAPI服务中具有类似于以下函数的功能:

public class TestController : ApiController
{
    public static async Task<string> Foo()
    {
        await Task.Delay(1).ConfigureAwait(false);
        return "";
    }

    public async static Task<string> Bar()
    {
        return await Foo();
    }

    public async static Task<string> Ros()
    {
        return await Bar();
    }

    // GET api/test
    public IEnumerable<string> Get()
    {
        Task.WaitAll(Enumerable.Range(0, 10).Select(x => Ros()).ToArray());

        return new string[] { "value1", "value2" }; // This will never execute
    }
}
公共类TestController:ApiController
{
公共静态异步任务Foo()
{
等待任务。延迟(1)。配置等待(false);
返回“”;
}
公共异步静态任务栏()
{
返回等待Foo();
}
公共异步静态任务Ros()
{
返回等待条();
}
//获取api/测试
公共IEnumerable Get()
{
Task.WaitAll(Enumerable.Range(0,10).Select(x=>Ros()).ToArray());
返回新字符串[]{“value1”,“value2”};//这将永远不会执行
}
}
其中
Get
将死锁


这是什么原因造成的?当我使用阻塞等待而不是
等待任务时,为什么这不会导致问题。延迟

等待
等待
虽然在概念上相似,但实际上完全不同

Wait
将同步阻止,直到任务完成。因此,当前线程实际上被阻塞,等待任务完成。一般来说,您应该一直使用“
async
”;也就是说,不要阻塞
async
code。在我的博客上,我详细介绍了我的工作

wait
将异步等待,直到任务完成。这意味着当前方法已“暂停”(其状态已捕获),并且该方法将不完整的任务返回给调用方。稍后,当
await
表达式完成时,该方法的剩余部分将被安排为继续

您还提到了一个“协作块”,我假设您的意思是您正在等待的任务可能会在等待线程上执行。有些情况下可能会发生这种情况,但这是一种优化。在许多情况下,它都不会发生,例如任务是为另一个计划程序执行的,或者它已经启动,或者它是一个非代码任务(例如在代码示例中:
Wait
无法内联执行
Delay
任务,因为它没有代码)


您可能会发现我的建议很有帮助。

根据我从不同来源读到的内容:

表达式不会阻止正在其上执行的线程。相反,它会导致编译器将
async
方法的其余部分注册为等待任务的延续。控件然后返回给
async
方法的调用方。当任务完成时,它调用它的continuation,并且
async
方法的执行在它停止的地方继续

要等待单个任务完成,可以调用其
任务.wait
方法。对
Wait
方法的调用会阻塞调用线程,直到单个类实例完成执行。无参数的
Wait()
方法用于无条件等待任务完成。该任务通过调用
线程.Sleep
方法睡眠两秒钟来模拟工作


这也是一本很好的读物。

其他答案中没有给出一些重要事实:

“异步等待”在CIL级别更为复杂,因此需要占用内存和CPU时间

如果等待时间不可接受,则可以取消任何任务

在“async await”的情况下,我们没有处理此类任务的处理程序来取消它或监视它

使用任务比“异步等待”更灵活

任何同步功能都可以通过async包装

public async Task<ActionResult> DoAsync(long id) 
{ 
    return await Task.Run(() => { return DoSync(id); } ); 
} 
公共异步任务

我不明白为什么我必须接受同步和异步方法的代码复制或使用黑客

结论:手工创建任务并进行控制效果更好。处理程序对任务给予更多的控制。我们可以监控和管理任务:


对不起,我的英语不太好。

我认为有一个误解,
Wait
工作正常
Wait
死锁。显然:是的,如果我用
Task.Delay(1)
替换我的
Wait任务。Delay(1)。Wait()
服务工作正常,否则它会死锁。不,任务调度程序不会这样做
Wait
阻塞线程,它不能用于其他事情。@ronag我猜你只是把方法名弄混了,你的死锁实际上是由阻塞代码引起的,并使用了
Wait
代码。要么是这样,要么是死锁与之无关,而您错误地诊断了问题。@hexterminator:这是设计的-它对UI应用程序非常有效,但却会妨碍ASP.NET应用程序。ASP.NET Core已通过删除同步上下文解决了此问题,因此ASP.NET Core请求中的阻止不再死锁。@Servy:我会在有时间时立即返回回购。目前,它与
Task.Delay(1.Wait()
一起工作,这已经足够好了。
Task.Delay(1.Wait()
基本上与
Thread.Sleep(1000)
完全相同。在实际的生产代码中,它很少合适。@ronag:Your
WaitAll
导致死锁。有关更多详细信息,请参见我回答中的我的博客链接。您应该使用
wait Task.whalll
。@ronag因为您有
configurewait(false)
Bar
Ros
的一次调用不会死锁,但是因为您有一个创建多个然后等待所有这些的可枚举项,所以第一个条将死锁第二个条。如果您
等待任务。当所有的
不是等待所有的任务,这样您就不会阻塞ASP上下文,您将看到该方法正常返回。@ronag您的另一个选项是在树上一直添加
。ConfigureAwait(false)
,直到您阻塞为止,这样就不会有任何东西试图返回主上下文;那就行了。另一种选择是启动内部同步上下文。如果将
任务.whalll
放入
异步泵中,请运行