我在“正确的方法”周围使用了引号,因为我已经清楚地知道使用异步API的正确方法是简单地让异步行为在整个调用链中传播。这不是一个选择。
我正在处理一个非常大而复杂的系统,该系统专门设计用于在循环中同步进行批处理。
突然使用HttpClient的原因是因为在此之前,用于批处理的所有数据都是从SQL数据库中收集的,现在我们将Web API调用添加到混合中。
是的,我们正在同步执行循环中调用Web API。我知道。重写整个事情是异步的不是一个选择。这实际上是我们想要做的。 (我们正在尽可能减少API调用的次数)
我实际上 did 试图在调用链上传播异步行为,但是后来我发现自己有50个文件有很深的变化,仍然有数百个编译器错误需要解决,并失去了希望。我被击败了。
然后,回到问题所在,考虑到微软的建议,从不使用WebRequest进行新开发,而改用HttpClient(仅提供异步API),我该怎么办?
这是我在做什么的伪代码...
foreach (var thingToProcess in thingsToProcess)
{
thingToProcess.ProcessStuff(); // This makes an API call
}
如何实现ProcessStuff()?
我的第一个实现看起来像这样
public void ProcessStuff()
{
var apiResponse = myHttpClient // this is an instance of HttpClient
.GetAsync(someUrl)
.Result;
// do some stuff with the apiResponse
}
但是,我被告知,由于同步上下文,从ASP.NET之类的方法中调用.Result可能会导致死锁。
猜怎么着,该批处理过程将从ASP.NET控制器开始。是的,再次,我知道,这很愚蠢。从ASP.NET运行时,它仅是“批处理”一项,而不是整个批处理,但是我离题了,仍然从ASP.NET调用它,因此我担心死锁。
那么解决这个问题的“正确方法”是什么?
答案 0 :(得分:14)
对于现在遇到此问题的任何人,.NET 5.0 已将同步 Send
方法添加到 HttpClient
。 https://github.com/dotnet/runtime/pull/34948
因此您可以使用它代替 SendAsync
。例如
public string GetValue()
{
var client = new HttpClient();
var webRequest = new HttpRequestMessage(HttpMethod.Post, "http://your-api.com")
{
Content = new StringContent("{ 'some': 'value' }", Encoding.UTF8, "application/json")
};
var response = client.Send(webRequest);
using var reader = new StreamReader(response.Content.ReadAsStream());
return reader.ReadToEnd();
}
此代码只是一个简化示例,尚未准备好用于生产。
答案 1 :(得分:3)
尝试关注-
var task = Task.Run(() => myHttpClient.GetAsync(someUrl));
task.Wait();
var response = task.Result;
仅在无法使用异步方法时才使用它。
此方法完全无死锁,如msdn博客上所述- https://blogs.msdn.microsoft.com/jpsanders/2017/08/28/asp-net-do-not-use-task-result-in-main-context/
答案 2 :(得分:1)
您也可以看看使用Nito.AsyncEx,它是一个nuget程序包。我听说过使用Task.Run()的问题,这个可以解决这个问题。这是api文档的链接: http://dotnetapis.com/pkg/Nito.AsyncEx/4.0.1/net45/doc/Nito.AsyncEx.AsyncContext
这是在控制台应用程序中使用异步方法的示例: https://blog.stephencleary.com/2012/02/async-console-programs.html