我已经在这上面敲了几个小时,我已经把手举到空中了。据我所知,我遇到了围绕HttpClient和异步的死锁。
目标是让一系列不相关的帖子快速连续发布,等待所有人完成,然后从结果集构建一个文档。该程序具有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,很久之后应该完成的代码已经完成。
我看了几十个问题&amp;关于SO和其他地方的答案,但他们并没有完全帮助找到问题。大多数人似乎都围绕着#34; .ConfigureAwait(false)&#34;在异步调用上,但它似乎没有任何影响。
答案 0 :(得分:2)
所以,在评论中@JSteward的帮助下,他指出async
和Parrallel.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
,Task
或Task<T>
的返回类型。