为什么要使用HttpClient进行同步连接


187

我正在建立一个与API交互的类库。我需要调用API并处理XML响应。我可以看到使用HttpClient进行异步连接的好处,但是我正在做的事情纯粹是同步的,所以我看不出使用会有什么明显的好处HttpWebRequest

如果有人能阐明任何想法,我将不胜感激。为此我不是使用新技术的人。


3
我不想告诉您,但是由于Windows网络内部的工作方式(也称为完成端口),因此HTTP调用永远不会完全同步。
TomTom


Answers:


370

但是我在做的事情完全是同步的

您可以使用HttpClient同步请求就好了:

using (var client = new HttpClient())
{
    var response = client.GetAsync("http://google.com").Result;

    if (response.IsSuccessStatusCode)
    {
        var responseContent = response.Content; 

        // by calling .Result you are synchronously reading the result
        string responseString = responseContent.ReadAsStringAsync().Result;

        Console.WriteLine(responseString);
    }
}

就您为什么应该使用HttpClientover WebRequest而言,HttpClient新孩子正在开发中,并且可能包含对旧客户的改进。


27
您异步使用异步方法是否可能会阻塞UI线程?string responseString = Task.Run(() => responseContent.ReadAsStringAsync()).Result;如果您必须使同步,则可能需要考虑类似的方法。
Earthling

13
@earthling,是的,Task.Run从ThreadPool调用任务,但是您正在调用.Result它杀死所有由此带来的好处,并阻塞了您在其中调用此线程的线程.Result(通常恰好是主UI线程)。
Darin Dimitrov 2014年

35
根据这篇文章(blogs.msdn.com/b/pfxteam/archive/2012/04/13/10293638.aspx)进行这样的调用.Result可能会耗尽线程池并导致死锁。
Pete Garafano 2014年

16
如果在new TaskFactory(TaskScheduler.FromCurrentSynchronizationContext()
Wim Coenen

23
那么,如何从UI线程同步使用HttpClient?假设我故意要阻塞UI线程(或者我正在编写一个控制台应用程序),直到得到HTTP响应为止。所以,如果Wait(),Result()等可能导致死锁,那么在没有这种情况的情况下,确切的解决方案是什么死锁的风险,并且使用WebClient等其他类?
德克斯特(Dexter)

26

我再次重申Donny V. Answer和Josh's

“我不使用异步版本的唯一原因是,如果我试图支持尚未内置异步支持的.NET较旧版本。”

(如果我有声誉,请投票。)

我不记得上一次(如果有的话),我很感谢HttpWebRequest抛出了状态代码> = 400的异常。要解决这些问题,您需要立即捕获异常,并将它们映射到某些非异常响应机制在您的代码中...本身无聊,乏味且容易出错。无论是与数据库通信还是实现定制的Web代理,“近乎”总是希望Http驱动程序仅告诉您的应用程序代码返回了什么,并由您决定如何执行。

因此,最好使用HttpClient。


1
我很惊讶它HttpClient本身是一个包装器HttpWebRequest(实际上,在内部捕获了这些WebException对象并HttpResponseMessage为您转换为a )。我本以为完全从头开始构建新客户端会更容易。

4
有很多很好的理由,例如不想只对非常低级的http调用重写整个代码库,而这甚至对性能没有要求(但是会引入异步到一百万个位置)。
FrankyBoy

在.net core 2中,如果要使用DynamicExpressionParser动态评估表达式,则可能无法使用异步。属性索引器不能使用异步;在我的情况下,我需要动态评估一个类似“ GetDefaultWelcomeMessage [\“ InitialMessage \”]“的字符串,其中此方法进行HttpCall,并且索引语法优于方法语法“ Util.GetDefaultWelcomeMessage(\” InitialMessage \“)”“
eugen

7

如果要构建类库,则库的用户可能希望异步使用您的库。我认为这是最大的原因。

您也不知道如何使用您的库。也许用户将处理大量的请求,并且异步执行将帮助它更快,更高效地执行。

如果您可以简单地做到这一点,请尽量不要让库用户负担繁重的工作,并尽量让他们异步处理。

我不使用异步版本的唯一原因是,如果我尝试支持尚未内置异步支持的旧版.NET。


我知道了,所以使类库异步,并允许系统用户决定是异步使用它,还是使用await并同步使用它?
番茄酱

erm,await通过将控制权返回给调用方来帮助使某些调用异步。
2013年

6

就我而言,接受的答案无效。我从没有异步动作的MVC应用程序调用API。

这是我设法使其工作的方式:

private static readonly TaskFactory _myTaskFactory = new TaskFactory(CancellationToken.None, TaskCreationOptions.None, TaskContinuationOptions.None, TaskScheduler.Default);
public static T RunSync<T>(Func<Task<T>> func)
    {           
        CultureInfo cultureUi = CultureInfo.CurrentUICulture;
        CultureInfo culture = CultureInfo.CurrentCulture;
        return _myTaskFactory.StartNew<Task<T>>(delegate
        {
            Thread.CurrentThread.CurrentCulture = culture;
            Thread.CurrentThread.CurrentUICulture = cultureUi;
            return func();
        }).Unwrap<T>().GetAwaiter().GetResult();
    }

然后我这样称呼它:

Helper.RunSync(new Func<Task<ReturnTypeGoesHere>>(async () => await AsyncCallGoesHere(myparameter)));

1
Thx @Darkonekt ...这对我来说非常有效。只有HttpClient.SendAsync(...)。Result在AspNet Handler(.ASHX)中永远无法运行。
拉斐尔·卡佐佐藤·思

3
public static class AsyncHelper  
{
    private static readonly TaskFactory _taskFactory = new
        TaskFactory(CancellationToken.None,
                    TaskCreationOptions.None,
                    TaskContinuationOptions.None,
                    TaskScheduler.Default);

    public static TResult RunSync<TResult>(Func<Task<TResult>> func)
        => _taskFactory
            .StartNew(func)
            .Unwrap()
            .GetAwaiter()
            .GetResult();

    public static void RunSync(Func<Task> func)
        => _taskFactory
            .StartNew(func)
            .Unwrap()
            .GetAwaiter()
            .GetResult();
}

然后

AsyncHelper.RunSync(() => DoAsyncStuff());

如果使用该类将async方法作为参数传递,则可以安全的方式从sync方法中调用async方法。

在这里解释:https : //cpratt.co/async-tips-tricks/


-1

所有答案似乎都集中在HttpClient同步使用上,而不是给出问题的实际答案。

HttpClient它不仅仅是一个简单的请求/响应处理程序,因此它可以处理不同网络的某些特殊情况。也就是说,在我的情况下,使用需要协商的NTLM代理,在客户端和代理服务器之间发送带有令牌和凭据的多个请求/响应以进行身份​​验证。HttpClient(使用HttpClientHandler)似乎具有内置的机制,该机制可以通过一种方法调用来返回超出代理的资源。


您的答案也没有说明如何异步使用HttpClient。
user275801

@ user275801这是愚蠢的评论。没有人问。默认情况下是异步的。
Bizniztime
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.