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
,为什么这不会引起问题呢?
Task.Delay(1).Wait()
基本上与Thread.Sleep(1000)
是完全相同的。在实际的生产代码中,它很少是合适的选择。 - ServyWaitAll
引起了死锁。请查看我回答中的博客链接获取更多详细信息。你应该使用await Task.WhenAll
代替。 - Stephen ClearyConfigureAwait(false)
。我不能一路使用异步,因为那将需要在我的实际代码中进行太多的代码更改。 - ronagConfigureAwait(false)
,所以单个对Bar
或Ros
的调用不会死锁,但是,由于您使用了一个可枚举类型,它会创建多个任务并等待所有这些任务完成,因此第一个任务将死锁第二个任务。如果您改用await Task.WhenAll
来等待所有任务完成,以避免阻塞 ASP 上下文,您将看到该方法正常返回。 - Servy.ConfigureAwait(false)
,直到阻塞,这样就不会再尝试返回到主上下文; 这将起作用。另一种选择是启动内部同步上下文。链接。如果你将Task.WhenAll
放入AsyncPump.Run
中,它将有效地阻塞整个过程,而无需在任何地方使用ConfigureAwait
, 但这可能是一个过于复杂的解决方案。 - Servy