C# 什么';这是“什么?”;“正确的道路”;要同步使用HttpClient?

C# 什么';这是“什么?”;“正确的道路”;要同步使用HttpClient?,c#,asynchronous,async-await,C#,Asynchronous,Async Await,我在“RightWay”周围使用引号,因为我已经很清楚使用异步API的正确方法是简单地让异步行为在整个调用链中传播。这不是我们的选择 我正在处理一个非常大和复杂的系统,专门设计用于在循环中同步进行批处理 我突然使用HttpClient的原因是,在此之前,批处理的所有数据都是从SQL数据库收集的,而现在我们正在向混合中添加一个Web API调用 是的,我们正在同步执行循环中调用Web API。我知道。将整个过程重写为异步不是一个选项。这实际上是我们想要做的。(我们正在尽可能减少API调用的数量)

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

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

我突然使用HttpClient的原因是,在此之前,批处理的所有数据都是从SQL数据库收集的,而现在我们正在向混合中添加一个Web API调用

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

事实上,我确实试图将异步行为传播到调用链的上游,但后来我发现自己有50个文件在深入修改,仍然有数百个编译器错误需要解决,并且失去了所有希望。我被打败了

那么,回到问题上来,考虑到微软建议在新开发中不要使用WebRequest,而是使用只提供异步API的HttpClient,我该怎么办

这是我正在做的一些伪代码

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
}
但是,有人告诉我,由于同步上下文的原因,以这种方式调用.Result可能会在从ASP.NET之类的东西调用它时导致死锁

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

那么“正确的方法”是什么呢?

试试下面的方法-

var task = Task.Run(() => myHttpClient.GetAsync(someUrl)); 
task.Wait();
var response = task.Result;
仅当无法使用异步方法时才使用它

这个方法是完全无死锁的,正如msdn博客中提到的-

您还可以考虑使用Nito.AsyncEx,这是一个nuget包。我听说过使用Task.Run()的问题,这就解决了这个问题。以下是api文档的链接:

下面是在控制台应用程序中使用异步方法的示例:

对于现在遇到此问题的任何人,.NET 5.0已经向
HttpClient
添加了一个同步
Send
方法

因此,您可以使用它而不是
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();
}

此代码只是一个简化的示例,尚未准备好生产。

可能的重复:可能的重复感谢您最终以MSDN帖子的形式对此提供了明确的答案。事实上,我在发布之前在谷歌上搜索了这篇文章,并在StackOverflow上发现了许多类似的问题,但没有一篇文章提供了MS的来源,明确指出在线程上运行任务、等待并在等待后获取结果是安全的。关于这个问题的许多其他SO线程实际上都有错误的答案作为公认的答案,但它看起来与正确的答案非常相似。也就是说,他们跳过对.Wait()的调用。这不起作用。
task.Wait()上的死锁2021更新:查看@alexs的另一个答案,我现在将其标记为已接受的答案。HttpClient API中添加了一个新的同步方法。@spoonraker我认为这仍然是您提出的特定问题的更好答案,这可能会吸引那些“使用.NET 5”不是合理答案的人来使用一段代码。对他们来说,这个问题的基本前提仍然非常重要:没有同步API,但替代方案不受欢迎。我怀疑许多使用.NET5的人是否有理由问这个问题。只是想一想。我哭b/c我们的企业软件仍然使用NetFX4.7