即使使用ConfigureAwait,也会在WebAPI死锁中同步调用异步方法(false)

时间:2017-02-04 02:30:04

标签: c# asynchronous asp.net-web-api async-await asp.net-web-api2

我有nuget包Esri.ArcGISRuntime,我需要在我的一个Web API 2控制器中调用方法QueryTask.ExecuteAsync。没有同步计数器部分所以在我的库c#代码中我使用的是

这样的包装器
    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);
    }

在我的程序/服务中完美运行。但是在Web API 2控制器中以这种方式使用ExecuteSync会导致它完全冻结并且永远不会返回响应。

我做了一些研究,并相信罪魁祸首在这里提到: http://blog.stephencleary.com/2012/07/dont-block-on-async-code.html

我绝对不想异步使用该功能。上面的函数是如此核心并且隐藏在4个包装器的深处,这将是我的库类的一个重大改进,冒泡异步方法只是为了支持这个web api调用。

我正在寻找围绕Web API的这种奇怪行为的解决方法/黑客/建议,以允许我同步运行此异步方法而不会使其死锁

2 个答案:

答案 0 :(得分:14)

  

我绝对不想异步使用该功能。

但是,我必须说出来。异步代码是最好的解决方案。您正在进行的操作是异步的,并且为其公开同步API充其量是有问题的。

需要时间吗?当然。但是你的代码会更好。

  

我正在寻找解决方法/黑客/建议

我有an entire article on the subject of brownfield async development,其中涵盖了所有已知的黑客及其缺点。

在您的特定情况下(从非核心ASP.NET上调用WebApi 考虑到它确实可以在Console / Win32Service风格的应用程序中运行),我会说Thread Pool Hack应该为你工作。它看起来像这样:

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

这个想法是ExecuteAsync在请求上下文之外的线程池线程上运行。然后阻止请求线程,直到异步工作完成。

答案 1 :(得分:0)

您正在调用的对象.Wait()on也是一项任务,您尚未对其进行配置。将.ConfigureAwait(false)添加到ExecuteQueryAsync的返回值。

或者,更好的是,WebAPI也可以是同步,因此整个堆栈异步会更好。