【发布时间】:2012-10-30 13:54:08
【问题描述】:
我不太明白Task.Wait 和await 之间的区别。
我在 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
}
}
Get 会在哪里死锁。
什么可能导致这种情况?当我使用阻塞等待而不是 await Task.Delay 时,为什么这不会导致问题?
【问题讨论】:
-
@Servy:我一有时间就会带回一个仓库。目前它适用于
Task.Delay(1).Wait(),这已经足够好了。 -
Task.Delay(1).Wait()基本上与Thread.Sleep(1000)完全相同。在实际的生产代码中,它很少适用。 -
@ronag:您的
WaitAll导致了死锁。有关更多详细信息,请参阅我的答案中指向我的博客的链接。你应该改用await Task.WhenAll。 -
@ronag 因为你有
ConfigureAwait(false)一个 single 调用Bar或Ros不会死锁,但因为你有一个可枚举的创建超过一个然后等待所有这些,第一个栏将死锁第二个。如果您await Task.WhenAll而不是等待所有任务,这样您就不会阻塞 ASP 上下文,您将看到该方法正常返回。 -
@ronag 你的另一个选择是添加
.ConfigureAwait(false)一直向上直到你阻塞,这样就没有什么永远试图回到主要背景;那行得通。另一种选择是启动内部同步上下文。 Link。如果您将Task.WhenAll放在AsyncPump.Run中,它将有效地阻止整个事情,而您无需在任何地方使用ConfigureAwait,但这可能是一个过于复杂的解决方案。
标签: c# task-parallel-library deadlock async-await