簡體   English   中英

Task.WaitAll() 死鎖

[英]Task.WaitAll() deadlocking

我想在 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響應,並且可能await讀取最終反序列化並返回的響應內容。

當我將測試中的foreach部分更改為以下內容時(即,不使用Task.WhenAll() / WaitAll() ),代碼運行時不會出現死鎖:

foreach (var led in ldapEntries)
{
    await _attributesServiceClient.CreateLdapEntry(led);
}

究竟發生了什么?

編輯:雖然這個問題已被標記為重復,但我看不出鏈接的問題與這個問題有什么關系。 鏈接中的代碼示例都使用.Result ,據我所知,它會阻止執行,直到任務完成。 相反, Task.WhenAll()返回一個可以等待並在所有任務完成后完成的任務。 那么為什么等待Task.WhenAll()死鎖呢?

您發布的代碼不可能具有所描述的行為。 第一次調用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 "";
}

Task.WaitAll()會死鎖,因為它在任務未完成時阻塞了當前線程(並且由於您使用的是async/await而不是線程,您的所有任務都在同一線程上運行,並且您不會讓您的等待的任務返回調用點,因為它們正在運行的線程 -與您調用Task.WaitAll()的線程相同- 被阻塞)。

不知道為什么WhenAll在這里也讓你陷入僵局,但絕對不應該。

PS:您不需要對async方法返回的任務調用Start :它們在創建時已經“熱”(已經啟動)

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM