简体   繁体   中英

await Task.WhenAll() appears to be deadlocking

In the following code, a deadlock happens on the Task.WhenAll line:

[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);
        creationTasks.Add(task);
    }
    await Task.WhenAll(creationTasks); // <- deadlock here

    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>();
        }

        return null;
    }
}

The xUnit test attempts to create test data asynchronously by calling an asynchronous method that itself await sa response from a web service. _httpClient is a real HttpClient created off an in-memory TestServer via TestServer.CreateClient() .

When setting a breakpoint on the using line in the CreateLdapEntry method, it is hit twice. A breakpoint on the status code check is never hit. When breaking on Task.WhenAll() and inspecting creationTasks , both tasks are in state WaitingForActivation :

creationTasks
Count = 2
    [0]: Id = 32, Status = WaitingForActivation, Method = "{null}", Result = "{Not yet computed}"
    [1]: Id = 33, Status = WaitingForActivation, Method = "{null}", Result = "{Not yet computed}"

When not using Task.WhenAll() but instead awaiting each task individually, no deadlock occurs:

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

I am aware that a similar question has been asked and answered, however the code examples there make use of .Result , not of await Task.WhenAll() .

I'd like to understand why that deadlock is occuring when using Task.WhenAll() .

EDIT: Added Call Stack of locked threads

Not Flagged     3992    11  Worker Thread   Worker Thread   Microsoft.AspNetCore.Routing.dll!Microsoft.AspNetCore.Builder.RouterMiddleware.Invoke
                        [Managed to Native Transition]
                        Microsoft.AspNetCore.Routing.dll!Microsoft.AspNetCore.Builder.RouterMiddleware.Invoke(Microsoft.AspNetCore.Http.HttpContext httpContext)
                        ShibbolethAttributes.Service.dll!RoleManager.Service.Middleware.ApiKeyHandlerMiddleware.Invoke(Microsoft.AspNetCore.Http.HttpContext context) Line 38
                        Microsoft.AspNetCore.Diagnostics.dll!Microsoft.AspNetCore.Diagnostics.DeveloperExceptionPageMiddleware.Invoke(Microsoft.AspNetCore.Http.HttpContext context)
                        System.Private.CoreLib.dll!System.Runtime.CompilerServices.AsyncMethodBuilderCore.Startd__7>(ref Microsoft.AspNetCore.Diagnostics.DeveloperExceptionPageMiddleware.d__7 stateMachine)
                        Microsoft.AspNetCore.Diagnostics.dll!Microsoft.AspNetCore.Diagnostics.DeveloperExceptionPageMiddleware.Invoke(Microsoft.AspNetCore.Http.HttpContext context)
                        Microsoft.AspNetCore.Hosting.dll!Microsoft.AspNetCore.Hosting.Internal.HostingApplication.ProcessRequestAsync(Microsoft.AspNetCore.Hosting.Internal.HostingApplication.Context context)
                        Microsoft.AspNetCore.TestHost.dll!Microsoft.AspNetCore.TestHost.TestServer.ApplicationWrapper.ProcessRequestAsync(Microsoft.AspNetCore.Hosting.Internal.HostingApplication.Context context)
                        Microsoft.AspNetCore.TestHost.dll!Microsoft.AspNetCore.TestHost.HttpContextBuilder.SendAsync.AnonymousMethod__0()
                        System.Private.CoreLib.dll!System.Runtime.CompilerServices.AsyncMethodBuilderCore.Startc__DisplayClass10_0.b__0>d stateMachine)
                        Microsoft.AspNetCore.TestHost.dll!Microsoft.AspNetCore.TestHost.HttpContextBuilder.SendAsync.AnonymousMethod__0()
                        System.Private.CoreLib.dll!System.Threading.Tasks.Task.InnerInvoke()
                        System.Private.CoreLib.dll!System.Threading.ExecutionContext.RunInternal(System.Threading.ExecutionContext executionContext, System.Threading.ContextCallback callback, object state)
                        System.Private.CoreLib.dll!System.Threading.Tasks.Task.ExecuteWithThreadLocal(ref System.Threading.Tasks.Task currentTaskSlot)
                        System.Private.CoreLib.dll!System.Threading.ThreadPoolWorkQueue.Dispatch()

Not Flagged     1496    10  Worker Thread   Worker Thread   Microsoft.AspNetCore.Routing.dll!Microsoft.AspNetCore.Builder.RouterMiddleware.Invoke
                        Microsoft.AspNetCore.Routing.dll!Microsoft.AspNetCore.Builder.RouterMiddleware.Invoke(Microsoft.AspNetCore.Http.HttpContext httpContext)
                        ShibbolethAttributes.Service.dll!RoleManager.Service.Middleware.ApiKeyHandlerMiddleware.Invoke(Microsoft.AspNetCore.Http.HttpContext context) Line 38
                        Microsoft.AspNetCore.Diagnostics.dll!Microsoft.AspNetCore.Diagnostics.DeveloperExceptionPageMiddleware.Invoke(Microsoft.AspNetCore.Http.HttpContext context)
                        System.Private.CoreLib.dll!System.Runtime.CompilerServices.AsyncMethodBuilderCore.Startd__7>(ref Microsoft.AspNetCore.Diagnostics.DeveloperExceptionPageMiddleware.d__7 stateMachine)
                        Microsoft.AspNetCore.Diagnostics.dll!Microsoft.AspNetCore.Diagnostics.DeveloperExceptionPageMiddleware.Invoke(Microsoft.AspNetCore.Http.HttpContext context)
                        Microsoft.AspNetCore.Hosting.dll!Microsoft.AspNetCore.Hosting.Internal.HostingApplication.ProcessRequestAsync(Microsoft.AspNetCore.Hosting.Internal.HostingApplication.Context context)
                        Microsoft.AspNetCore.TestHost.dll!Microsoft.AspNetCore.TestHost.TestServer.ApplicationWrapper.ProcessRequestAsync(Microsoft.AspNetCore.Hosting.Internal.HostingApplication.Context context)
                        Microsoft.AspNetCore.TestHost.dll!Microsoft.AspNetCore.TestHost.HttpContextBuilder.SendAsync.AnonymousMethod__0()
                        System.Private.CoreLib.dll!System.Runtime.CompilerServices.AsyncMethodBuilderCore.Startc__DisplayClass10_0.b__0>d stateMachine)
                        Microsoft.AspNetCore.TestHost.dll!Microsoft.AspNetCore.TestHost.HttpContextBuilder.SendAsync.AnonymousMethod__0()
                        System.Private.CoreLib.dll!System.Threading.Tasks.Task.InnerInvoke()
                        System.Private.CoreLib.dll!System.Threading.ExecutionContext.RunInternal(System.Threading.ExecutionContext executionContext, System.Threading.ContextCallback callback, object state)
                        System.Private.CoreLib.dll!System.Threading.Tasks.Task.ExecuteWithThreadLocal(ref System.Threading.Tasks.Task currentTaskSlot)
                        System.Private.CoreLib.dll!System.Threading.ThreadPoolWorkQueue.Dispatch()

(Conclusion summarised from the comments, for any future viewers).

You're not blocking anywhere, so the only possible explanation I can think of is that at least one of the requests isn't completing.

(If you were synchronously waiting for a Task to complete then I could well expect a deadlock, since you're not using ConfigureAwait(false) , but since you only ever await your Tasks, I pretty sure this isn't the cause).

Given that your requests complete successfully when they're run individually, this implies that there's some concurrency issue when multiple requests are run in parallel, possibly to do with whatever _httpClient is, or to do with the server that requests are being made against (if they're running against a real server).

Given that your Task list doesn't show anything interesting, I'm inclined to think that one of the requests has synchronously blocked the thread that called into it.

Just see whether one of the requests has synchronously blocked. Open the Threads window, and double-click each thread in turn. Most won't be doing anything, but at least one might be running your code, or running a method called from your code. Look at the call stack to try and find out what's going on. You can double-click entries in the call stack to inspect variables in scope at each point.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM