还有另一个HttpClient /异步死锁

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了还有另一个HttpClient /异步死锁相关的知识,希望对你有一定的参考价值。

我几个小时以来一直在敲我的头,我已经把手举到这个空中了。据我所知,我遇到了围绕HttpClient和Async的死锁。

目标是让一系列不相关的帖子快速连续发布,等待所有人完成,然后从结果集构建一个文档。该程序有一个WPF UI,这是由按钮触发的:

private async void Generate_Suite_BTN_Click(object sender, RoutedEventArgs e)
{
    var suiteBuilder = new SuiteBuilder();
    await Task.Run(() => suiteBuilder.worker_Run());
}

这会触发worker_Run(),它有一些切换逻辑,并最终导致命中具有Parrallel.Foreach的SendFiles(),因为发送文件不需要是顺序的,并且彼此无关:

private bool SendFiles()
{
    var result = Parallel.ForEach(_InfoCollection, SendFile);
    return result.IsCompleted;
}

其中的每一个(并行)都在SendFile()中等待,它也有一些切换逻辑,基于我们发送的内容,但最终归结为:

var result = await Client.SendMessage ( vars );
results.Add(result.MessageId, result.StatusCode, result.HttpReason);

这是SendMessage()中的HttpClient部分:

public async Task<Result> SendMessage(vars)
{
    var soapResponse = new XmlDocument();
    try
    {
        Client.DefaultRequestHeaders.Add("SOAPAction", soapAction);
        Client.Timeout = TimeSpan.FromSeconds(Timeout);
        var content = new StringContent(soapRequest, Encoding.UTF8, contentType);
        var post =  await Client.PostAsync(url, content).ConfigureAwait(false);
        var response = post.Content;
        result.StatusCode = post.StatusCode;
        result.HttpReason = post.ReasonPhrase;
        var sResponse = await response.ReadAsStringAsync().ConfigureAwait(false);
        soapResponse.Load(sResponse);
    }
    catch (Exception ex)
    {
        //Catch logic
    }
}

我可以看到请求和响应与Fiddler来回传递,但是我遇到了逐行调试的麻烦,因为只要我点击PostAsync,VS就会一直翻转并一直持续到程序结束,跳过所有断点。同时,请求超时后会出现TaskCanceledException,很久之后应该完成的代码已经完成。

我在SO和其他地方看了几十个问答,但他们只是没有帮助找到问题。大多数似乎都集中在Async调用上的“.ConfigureAwait(false)”的自由散布上,但它似乎并没有任何影响。

答案

所以,在评论中@JSteward的帮助下,他指出asyncParrallel.ForEach不太适合一起工作,因为应该避免在处理异步时返回类型的void

他建议我只使用Async,从顶部(按钮点击)到底部(消息发送),然后就可以了。感谢他的指导。

这个链接有助于解释为什么会这样:Async/Await - Best Practices in Asynchronous Programming

SendFiles最终看起来像这样:

private async Task<bool> SendFiles()
{
    var result = _InfoCollection.Select(SendFile);
    await Task.WhenAll(result).ConfigureAwait(false);
    return true;
}

所有其他方法去了async,有适当的awaits,返回类型的TaskTask<T>

以上是关于还有另一个HttpClient /异步死锁的主要内容,如果未能解决你的问题,请参考以下文章

并行 for 循环和 httpclient 死锁并引发异常

不允许 httpclient 发布异步返回方法 (405)

抓住异步编程async/await语法糖的牛鼻子

如何使用异步发布并使用 HttpClient 等待

异步等待 HttpClient.PostAsync 调用

阻塞与非阻塞异步代码