【发布时间】:2019-02-19 14:50:55
【问题描述】:
我想在 xUnit 测试中多次调用异步方法并等待所有调用完成,然后再继续执行。我读到我可以在这种情况下使用Task.WhenAll() 和Task.WaitAll()。然而,由于某种原因,代码出现了死锁。
[Fact]
public async Task GetLdapEntries_ReturnsLdapEntries()
{
var ldapEntries = _fixture.CreateMany<LdapEntryDto>(2).ToList();
var creationTasks = new List<Task>();
foreach (var led in ldapEntries)
{
var task = _attributesServiceClient.CreateLdapEntry(led);
task.Start();
creationTasks.Add(task);
}
Task.WaitAll(creationTasks.ToArray()); //<-- deadlock(?) here
//await Task.WhenAll(creationTasks);
var result = await _ldapAccess.GetLdapEntries();
result.Should().BeEquivalentTo(ldapEntries);
}
public async Task<LdapEntryDto> CreateLdapEntry(LdapEntryDto ldapEntryDto)
{
using (var creationResponse = await _httpClient.PostAsJsonAsync<LdapEntryDto>("", ldapEntryDto))
{
if (creationResponse.StatusCode == HttpStatusCode.Created)
{
return await creationResponse.Content.ReadAsAsync<LdapEntryDto>();
}
throw await buildException(creationResponse);
}
}
被测系统是一个封装了 HttpClient 的包装器,它调用 Web 服务,awaits 响应,并且可能 awaits 读取响应的内容,最终反序列化并返回。
当我将测试中的foreach 部分更改为以下(即不要使用Task.WhenAll() / WaitAll())时,代码运行时没有死锁:
foreach (var led in ldapEntries)
{
await _attributesServiceClient.CreateLdapEntry(led);
}
到底发生了什么?
编辑:虽然这个问题已被标记为重复,但我看不出链接的问题与这个问题有何关系。链接中的代码示例都使用.Result,据我所知,它会阻止执行,直到任务完成。相反,Task.WhenAll() 返回一个可以等待并在所有任务完成后完成的任务。那么为什么等待Task.WhenAll()死锁呢?
【问题讨论】:
-
@Servy 感谢您的链接,但我看不到与
Task.WhenAll() / WaitAll()的关系? -
副本准确地解释了为什么会发生死锁,以及如何处理它。只需阅读答案即可。
-
这就是我在评论之前所做的,我仍然看不到与
WaitAll或WhenAll的关系,后者创建了一个等待的任务并且仍然死锁;我不在任何地方使用.Result。遗憾地告诉我“再读一遍”对我没有帮助。 -
您在使用 Task.WaitAll 时是否收到编译器警告?我怀疑你是,因为你的方法被标记为异步。它不是在等待。它也没有返回一个任务,所以它看起来甚至不会编译。无论如何,绝对不应该使用 Task.WaitAll。这是什么类型的应用程序(ASP.NET、ASP.NET Core、WPF、Win Forms 等...)?
-
@mason 哪一个?所有方法都返回
Task或Task<T>。Task.WaitAll()等不及了。我没有收到任何编译器警告或错误,此代码工作正常(死锁除外,使用备用foreach版本按预期工作)。
标签: c# async-await task