我想在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服务,await
s响应,并且可能await
读取响应的内容,并最终反序列化并返回
当我将测试中的foreach
部分更改为以下内容(即,不使用Task.WhenAll() / WaitAll()
)时,代码正在运行而没有死锁:
foreach (var led in ldapEntries)
{
await _attributesServiceClient.CreateLdapEntry(led);
}
到底发生了什么?
编辑:虽然此问题被标记为重复,但我看不到链接的问题与该问题的关系。链接中的所有代码示例均使用.Result
,据我所知,它会阻塞执行直到任务完成。相反,Task.WhenAll()
返回的任务可以等待,并且在所有任务完成后才完成。那么为什么要等待Task.WhenAll()
陷入僵局?
答案 0 :(得分:1)
您发布的代码可能无法描述行为。对Task.Start
的第一次调用会抛出InvalidOperationException
,但测试失败。
我了解到我可以在这种情况下使用Task.WhenAll()和Task.WaitAll()。
否;要异步地等待多个任务,必须使用Task.WhenAll
,而不是Task.WaitAll
。
示例:
[Fact]
public async Task GetLdapEntries_ReturnsLdapEntries()
{
var ldapEntries = new List<int> { 0, 1 };
var creationTasks = new List<Task>();
foreach (var led in ldapEntries)
{
var task = CreateLdapEntry(led);
creationTasks.Add(task);
}
await Task.WhenAll(creationTasks);
}
public async Task<string> CreateLdapEntry(int ldapEntryDto)
{
await Task.Delay(500);
return "";
}
答案 1 :(得分:0)
Task.WaitAll()
会死锁的原因仅仅是因为它在任务未完成时阻塞了当前线程(并且由于您使用的是async/await
而非线程,因此所有任务都是在同一线程上运行,并且您不让等待的任务返回到调用点,因为它们在-与您调用Task.WaitAll()
-相同的线程中运行-阻止)。
虽然不确定WhenAll
为何在这里也对您造成僵局,但绝对不应该。
PS:您无需在Start
方法返回的任务上调用async
:它们在创建时就已经“热”了(已经开始)