关于c#:await vs Task.Wait – 死锁?

await vs Task.Wait - Deadlock?

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

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
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
    }
}

哪里Get会死锁。

什么可能导致这个? 当我使用阻塞等待而不是await Task.Delay时,为什么这不会导致问题?


Waitawait - 虽然概念上相似 - 实际上完全不同。

Wait将同步阻止,直到任务完成。因此,当前线程被字面上阻止等待任务完成。作为一般规则,你应该使用"async一直向下";也就是说,不要阻止async代码。在我的博客上,我详细介绍了异步代码中的阻塞如何导致死锁。

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

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

您可能会发现我的async / await介绍很有帮助。


基于我从不同来源阅读的内容:

await表达式不会阻止它正在执行的线程。相反,它会导致编译器将其余的异步方法注册为等待任务的延续。然后,Control返回到异步方法的调用者。当任务完成时,它会调用它的继续,异步方法的执行从它停止的地方恢复。

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

这篇文章也很好读。