使用async/await而没有使用Task.Result会导致死锁

3

我有一个使用async/await的Web API:

public class SampleController : ApiController
{
    public async Task<IEnumerable<DocumentModel>> GetAll()
    {
        List<DocumentModel> modelItems = new List<DocumentModel>();

        var response = await SearchDocumentsAsync();
        var items = response.Results.ToList();
        foreach(var document in documents)
        {
            var model = new DocumentModel()
            {
                Id = document.Id,
                Name = document.Name
            };

            modelItems.Add(model);
        }

        return modelItems;
    }

    private Task<DocumentSearchResponse<Document>> SearchDocumentsAsync()
    {
        using (var searchClient = new SearchServiceClient(new SearchCredentials(searchServiceKey), searchServiceUri))
        using (var indexClient = searchClient.Indexes.GetClient(indexName))
        {
            var parameters = new SearchParameters()
            {
                IncludeTotalResultCount = true
            };

            // call the Azure Search service
            return indexClient.Documents.SearchAsync<Document>(search.Term, parameters);
        }
    }    
}

我在调用堆栈中没有使用.Wait().Result。这段代码是否仍然可能出现死锁?当我调试这段代码时,似乎确实出现了死锁,但就是想不出为什么。
我使用Postman发送了几个请求,有时候响应永远不会返回。如果我在调试器中按下暂停按钮,它将显示一行绿色的文本,指示“此线程从当前函数返回时要执行的下一条语句”。当我按下暂停按钮时,代码所处位置并不一致,但似乎每隔一个请求就会被阻塞一次。
上述代码是否可能导致死锁?

1
当你调试代码时,执行会在哪里停止?您还需要提供一个完整的示例,以便我们能够复制问题并提供解决方案。 - Servy
2
话虽如此,死锁可能由许多因素引起,同步地阻塞同步上下文而异步操作正在试图将继续操作发布到它是只是其中一种方式。还有很多其他的因素。 - Servy
我提到它停止的位置不一致。我更感兴趣的是上面的代码。上面的代码中是否有任何会导致死锁的操作? - Dismissile
你基本上没有展示任何代码;因此无法判断是否存在问题。 - Servy
@Servy 我更新了代码。我正在使用Azure Search REST API。我使用.NET库调用REST API并等待调用...然后我使用一些get/set属性从文档中返回的内容构建模型。 - Dismissile
1个回答

5
SearchDocumentsAsync中,你立即从SearchAsync返回任务。
你应该等待这个任务以确保在操作完成之前不会释放indexClientsearchClient。这些被释放可能是导致死锁的原因。

如果我更改代码,在SearchDocumentsAsync方法中添加async/await而不是返回任务,但我删除using语句并在ApiController.Dispose方法中Dispose它们,我会遇到类似的问题吗? - Dismissile
@Dismissile 我对MVC不够了解,多个线程能同时调用SearchDocumentsAsync的同一个实例吗?如果可以,那么你可能会遇到问题。但是,如果只有一个线程访问该实例,那么您应该可以将创建移到构造函数中,并将释放资源移到类的Dispose()方法中。注意:如果您有不需要创建客户端的方法,则可能会在其他调用中引入不必要的开销。 - Scott Chamberlain
@ScottChamberlain 在MVC中,每个请求都有自己的实例,因此这并不是真正可能的。 - Dismissile

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接