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



我有nuget包Esri.ArcGISRuntime,我需要在我的一个Web API 2控制器中调用方法QueryTask.ExecuteAnc。没有同步计数器部分,所以在我的库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这种奇怪行为的工作回合/破解/建议,以允许我同步运行这种异步方法,而不会使其死锁

我绝对不想异步使用该函数。

不过我不得不这么说。异步代码是最好的解决方案。您正在执行的操作是异步的,并且为其公开同步API充其量是有问题的。

这需要时间吗?当然但你的代码会因此变得更好。

我正在寻找解决方法/技巧/建议

我有一整篇关于棕地异步开发的文章,其中我涵盖了所有已知的技巧及其缺点。

在您的特定情况下(从非核心ASP.NET上的WebApi调用,考虑到它确实可以从Console/Win32Service风格的应用程序中工作),我认为线程池黑客应该适合您。它看起来像这样:

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

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

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

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

最新更新