简体   繁体   中英

Deadlock when calling an async method inside a non async action

We have a web application which consists of a Web API consumed by our UI. The web application is installed on different servers (tests before installing it by our clients).

On one server, we get a deadlock by the following endpoint:

[HttpGet, Route(Order = 1)]
public IHttpActionResult GetUserDetails(long userKey)
{
    var userService = ResolveService<IUserService>();
    var nameService =  ResolveService<INameService >();

    LoggInfo("Start userService.Get()"); //logged in logs
    var result = userService.Get(userKey);
    this.LoggInfo($"End userService.Get() with id = "{result.Id}); // logged in logs

    try
    {
      LoggInfo($"Start nameService.Get()"); // logged in logs
      result.Name =  nameService.Get(result.Namekey).Result?.Name;
      LoggInfo($"End nameService.Get()"); // not logged in logs
    }
   catch(Exception ex)
   {
     LogError("An error occured in NameService"); // not logged in logs
   }

    return Ok(result);
}

nameService.Get(id) is an async method:

public async Task<NameDTO> GetAsync(long key)
{
    LogInfo("start NameService.Get()"); // not logged in logs
    var query = GetQuery();
    var name = await query.Select(MapName())
                          .FirstOrDefaultAsync(n => n.Key == key);

    return name;
}

When I remove the async signature everything is working as expected, according to this article it is normal to have a deadlock.

Could you please explain me why this works in the other servers?

Thanks in advance for your help

Could you please explain me why this works in the other servers?

In order for the deadlock to occur, there are three necessary parts:

  1. A one-thread-at-a-time context, which all servers would have (the pre-Core ASP.NET context).
  2. An await (without ConfigureAwait(false) ) that acts asynchronously .
  3. Code elsewhere that blocks a thread inside that context, waiting for the async method to finish. All servers also have this.

Most likely, the difference in behavior is due to the second part: an await that acts asynchronously. await first examines its awaitable (eg, the Task passed to it), and if that awaitable is complete, then it continues executing the async method synchronously .

Specifically, this would happen if the Task<NameDTO> returned from GetAsync completed before the calling code hit the .Result . This would have to be extremely fast, but it's possible depending on caching, how fast your network hop is, how much the code is slowed down by load/neighbors/anti-virus, etc.

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