同步使用HttpClient的“正确方法”是什么?

spo*_*ker 5 c# asynchronous async-await

我在“正确的方法”周围使用了引号,因为我已经清楚地知道使用异步API的正确方法是简单地让异步行为在整个调用链中传播。这里不是一个选择。

我正在处理一个非常大而复杂的系统,该系统专门设计用于在循环中同步进行批处理。

我突然使用HttpClient的原因是因为在此之前,用于批处理的所有数据都是从SQL数据库中收集的,而现在我们将Web API调用添加到了混合中。

是的,我们正在同步执行循环中调用Web API。我知道。重写整个事情是异步的不是一个选择。这实际上是我们想要做的。(我们正在尽可能减少API调用的次数)

实际上,我确实尝试在调用链上传播异步行为,但是后来我发现自己有50个文件有很深的变化,仍然有数百个编译器错误需要解决,并失去了所有希望。我被打败了。

因此,回到问题所在,考虑到Microsoft的建议,从不使用WebRequest进行新开发,而改用HttpClient(仅提供异步API),我该怎么办?

这是我在做什么的伪代码...

foreach (var thingToProcess in thingsToProcess)
{
    thingToProcess.ProcessStuff(); // This makes an API call
}
Run Code Online (Sandbox Code Playgroud)

如何实现ProcessStuff()?

我的第一个实现看起来像这样

public void ProcessStuff()
{
    var apiResponse = myHttpClient // this is an instance of HttpClient
        .GetAsync(someUrl)
        .Result;

    // do some stuff with the apiResponse
}
Run Code Online (Sandbox Code Playgroud)

但是,我被告知,由于同步上下文而从ASP.NET之类的方法调用.Result时,以这种方式调用它可能导致死锁。

猜猜是什么,该批处理过程将从ASP.NET控制器开始。是的,再次,我知道,这很愚蠢。从ASP.NET运行时,它仅是“批处理”一项,而不是整个批处理,但是我离题了,它仍然从ASP.NET调用,因此我担心死锁。

那么解决这个问题的“正确方法”是什么?

ale*_*exs 16

对于现在遇到此问题的任何人,.NET 5.0 已将同步Send方法添加到HttpClient. https://github.com/dotnet/runtime/pull/34948

因此,您可以使用 this 而不是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();
}
Run Code Online (Sandbox Code Playgroud)

这段代码只是一个简化的例子,它不是生产就绪的。

  • 事实上,它并没有以某种方式向后移植到 .NET Framework,而它实际上非常有用,这是非常非常令人沮丧的。 (8认同)
  • 我哭了,因为我们的企业软件仍然使用 netfx 4.7 (2认同)
  • .NET 一如既往地优雅美丽。httpClient.Send(url) 肯定太麻烦了 (2认同)

kam*_*eet 12

尝试以下-

var task = Task.Run(() => myHttpClient.GetAsync(someUrl)); 
task.Wait();
var response = task.Result;
Run Code Online (Sandbox Code Playgroud)

仅在无法使用异步方法时才使用它。

如msdn博客-https : //blogs.msdn.microsoft.com/jpsanders/2017/08/28/asp-net-do-not-use-task-result-in-main-所述,此方法完全没有死锁 上下文/

  • 这是行不通的。`task.Wait();` 上出现死锁 (8认同)
  • @spoonraker 我认为这仍然是您提出的具体问题的更好答案,这可能会吸引那些“使用 .NET 5”不是让一小段代码正常工作的合理答案的人。对于他们来说,这个问题的基本前提仍然非常相关:没有同步 API,但替代方案已被弃用。我怀疑许多使用 .NET 5 的人是否有理由问这个问题。只是一个想法。 (4认同)
  • 感谢您最终以 MSDN 帖子的形式对此提供了明确的答案。我实际上在发布之前搜索了这个,并在 StackOverflow 上发现了许多类似的问题,但他们都没有提供来自 MS 的来源,明确说明在线程上运行任务,等待并在等待后获取结果是安全的。关于这个问题的许多其他 SO 线程实际上将错误答案作为已接受的答案,但它看起来与正确答案非常相似。也就是说,他们跳过对 .Wait() 的调用 (3认同)