简体   繁体   中英

Synchronously calling async method in WebAPI deadlocks even with ConfigureAwait(false)

I have the nuget package Esri.ArcGISRuntime and I need to call the method QueryTask.ExecuteAsync in one of my Web API 2 controllers. There is no synchronous counter part so in my library c# code I am using a wrapper like

    private QueryResult ExecuteSync()
    {
        var queryResults = ExecuteAsync();
        queryResults.Wait();
        return queryResults.Result;
    }

    private async Task<QueryResult> ExecuteQueryTaskAsync()
    {
        var queryTask = new QueryTask(_uri);
        return await queryTask.ExecuteAsync(_query).ConfigureAwait(false);
    }

Which works perfectly in my program/service. But using ExecuteSync this way in a Web API 2 controller causes it to completely freeze up and never return a response.

I have done some research and believe the culprit is mentioned here: http://blog.stephencleary.com/2012/07/dont-block-on-async-code.html

I absolutely do not want to use the function asynchronously. The above functions are so core and hidden deep within like 4 wrappers, that it would be a major overhaul of my library class to bubble up async methods just to support this one web api call.

I am looking for work-arounds/hacks/suggestions around this weird behavior of web API to allow me to run this async method synchronously and not have it deadlock

I absolutely do not want to use the function asynchronously.

I have to say it, though. Asynchronous code is the best solution. The operation you're doing is asynchronous, and exposing a synchronous API for it is problematic at best.

Does it take time? Sure. But your code will be better off for it.

I am looking for work-arounds/hacks/suggestions

I have an entire article on the subject of brownfield async development , where I cover all the known hacks along with their drawbacks.

In your particular case (calling from WebApi on non-Core ASP.NET and considering that it does work from a Console/Win32Service-style app), I'd say the Thread Pool Hack should work for you. It looks like this:

private QueryResult ExecuteSync()
{
  return Task.Run(() => ExecuteAsync()).GetAwaiter().GetResult();
}

The idea is that ExecuteAsync is run on a thread pool thread, outside of the request context. The request thread is then blocked until the asynchronous work completes.

The object you are calling .Wait() on is also a task, and you have not configured it. Add .ConfigureAwait(false) to the return value from ExecuteQueryAsync.

Or, better yet, WebAPI can be a sync as well, so making the whole stack async will be better overall.

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