我正在尝试调用某个服务,但该服务的每个请求的最大长度为,因此我将请求拆分为多个较小的请求。
然后我尝试将HttpClient与Await一起用作async
public async Task<string> CallGenoskanAsync(List<string> requestList)
{
ServicePointManager.ServerCertificateValidationCallback = delegate { return true; };
var credentials = new NetworkCredential(userId, password);
var tasks = new List<Task<string>>();
foreach (string requestString in requestList)
{
using (HttpClientHandler handler = new HttpClientHandler { Credentials = credentials })
{
using (HttpClient client = new HttpClient(handler))
{
client.BaseAddress = new Uri(baseAddress);
using (HttpResponseMessage response = client.GetAsync(requestString).Result)
using (HttpContent content = response.Content)
{
tasks.Add(content.ReadAsStringAsync());
}
}
}
}
Task.WaitAll(tasks.ToArray());
var result = "";
foreach (var task in tasks)
{
result += task.Result;
}
return result;
}
当await client.GetAsync被调用时,此代码死锁,它永远不会完成。
如果我将该行更改为
using (HttpResponseMessage response = client.GetAsync(requestString).Result)
然后我没有遇到任何僵局,我想我正在和foreach一起使用等待,但我无法弄清楚如何
编辑:更改了示例代码
答案 0 :(得分:1)
编译器会为您发布的代码发出警告;特别是,它会指出CallGenoskanAsync
是同步的,而不是异步的。
核心问题是这一行:
Task.WaitAll(tasks.ToArray());
When you're writing asynchronous code, you shouldn't block on it. To do so can cause deadlocks,正如我在博客文章中解释的那样。发生死锁是因为await
将捕获&#34; context&#34;它用于恢复执行async
方法。这个&#34;上下文&#34;是SynchronizationContext.Current
或TaskScheduler.Current
,许多上下文(特别是UI和ASP.NET请求上下文)只允许一个线程。因此,当您的代码阻塞某个线程(Task.WaitAll
)时,它会在该上下文中阻塞一个线程,这会阻止await
继续,因为它正在等待该上下文。
要修复,请使代码始终保持异步。我在async
简介中解释了the asynchronous equivalent of Task.WaitAll
is await Task.WhenAll
:
await Task.WhenAll(tasks);
WhenAll
还具有很好的属性,可以为您解包结果,因此您不必使用有问题的Result
属性:
var results = await Task.WhenAll(tasks);
return string.Join("", results);
答案 1 :(得分:0)
未提供相关背景:
如果
DoSomething().Result
或DoSomething.Wait()
)SynchronizationContext
的线程关联(IE,这几乎在任何地方运行,但控制台应用程序),然后强>
您的同步上下文的线程可能会在根Result / Wait()上被阻止,因此它永远不会处理已完成的对GetAsync()调度的调用的延续。死锁。
如果您满足上述条件,请尝试将.ConfigureAwait(false)应用于您等待的GetAsync。这将使延续被安排到Threadpool线程。请注意,您可能会在此时安排到其他线程。