Tại sao nên sử dụng HttpClient cho kết nối đồng bộ


187

Tôi đang xây dựng một thư viện lớp để tương tác với API. Tôi cần gọi API và xử lý phản hồi XML. Tôi có thể thấy những lợi ích của việc sử dụng HttpClientcho kết nối không đồng bộ, nhưng những gì tôi đang làm hoàn toàn đồng bộ, vì vậy tôi không thể thấy bất kỳ lợi ích đáng kể nào khi sử dụng HttpWebRequest.

Nếu bất cứ ai có thể làm sáng tỏ tôi sẽ đánh giá rất cao nó. Tôi không phải là một trong những người sử dụng công nghệ mới vì lợi ích của nó.


3
Tôi ghét phải nói với bạn, nhưng một cuộc gọi qua HTTP không bao giờ hoàn toàn đồng bộ do cách thức hoạt động của mạng windows bên trong (còn gọi là cổng hoàn thành).
TomTom


Câu trả lời:


370

nhưng những gì tôi đang làm hoàn toàn đồng bộ

Bạn có thể sử dụng HttpClientcho các yêu cầu đồng bộ tốt:

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);
    }
}

Theo như lý do tại sao bạn nên sử dụng HttpClienthơnWebRequest thì có liên quan, HttpClientlà đứa trẻ mới trong khối và có thể chứa những cải tiến so với máy khách cũ.


27
Việc sử dụng đồng bộ các phương thức async của bạn có khả năng chặn luồng UI của bạn không? Bạn có thể muốn xem xét một cái gì đó giống như string responseString = Task.Run(() => responseContent.ReadAsStringAsync()).Result;thay vào đó nếu bạn phải thực hiện điều này đồng bộ.
đất

13
@earthling, vâng, Task.Rungọi nhiệm vụ từ ThreadPool, nhưng bạn đang kêu gọi .Resultnó giết tất cả lợi ích từ việc này và chặn luồng mà bạn gọi đây .Result(thường là chủ đề UI chính).
Darin Dimitrov

35
Theo bài đăng này ( blog.msdn.com/b/pfxteam/archive/2012/04/13/10293638.aspx ) gọi .Resultnhư thế này có thể làm cạn kiệt luồng và gây ra bế tắc.
Pete Garafano

16
Mã này sẽ luôn luôn bế tắc nếu được thực thi trong một tác vụ được tạo trên luồng UI chính bởinew TaskFactory(TaskScheduler.FromCurrentSynchronizationContext()
Wim Coenen

23
Vậy, làm cách nào để sử dụng đồng bộ hóa httpClient từ luồng UI? Giả sử tôi cố tình chặn luồng UI (hoặc tôi đang viết ứng dụng bảng điều khiển) cho đến khi tôi nhận được phản hồi HTTP ... Vì vậy, nếu Wait (), result (), v.v. có thể gây ra bế tắc, thì giải pháp rõ ràng cho việc này là gì mà không có nguy cơ bế tắc và không sử dụng các lớp khác như WebClient?
Dexter

26

Tôi muốn nhắc lại câu trả lời của Donny V. và Josh's

"Lý do duy nhất tôi không sử dụng phiên bản async là nếu tôi đang cố gắng hỗ trợ phiên bản .NET cũ hơn chưa được tích hợp hỗ trợ async."

(và upvote nếu tôi có danh tiếng.)

Tôi không thể nhớ lần cuối cùng bao giờ, tôi rất biết ơn về việc httpWebRequest đã ném ngoại lệ cho mã trạng thái> = 400. Để giải quyết các vấn đề này, bạn cần nắm bắt các ngoại lệ ngay lập tức và ánh xạ chúng đến một số cơ chế phản hồi không ngoại lệ trong mã của bạn ... nhàm chán, tẻ nhạt và dễ bị lỗi. Cho dù nó đang liên lạc với cơ sở dữ liệu hoặc triển khai proxy web bespoke, 'trình điều khiển http' luôn luôn mong muốn rằng trình điều khiển HTTP chỉ trả về mã ứng dụng của bạn những gì được trả về và để bạn quyết định cách xử lý.

Do đó, httpClient là tốt hơn.


1
Tôi đã ngạc nhiên rằng HttpClientchính nó là một trình bao bọc xung quanh HttpWebRequest(thực sự, trong nội bộ bắt những WebExceptionđối tượng đó và thực hiện chuyển đổi thành một HttpResponseMessagecho bạn). Tôi đã nghĩ rằng việc xây dựng một khách hàng mới hoàn toàn từ đầu sẽ dễ dàng hơn.
Đại

3
Có rất nhiều lý do chính đáng như không muốn viết lại toàn bộ cơ sở mã của bạn chỉ cho một cuộc gọi http ở mức độ rất thấp, thậm chí không thực hiện quan trọng (nhưng sẽ giới thiệu không đồng bộ đến một triệu địa điểm).
FrankyBoy

Trong .net core 2 nếu bạn muốn đánh giá động một biểu thức với DynamicExpressionParser, có thể không thể sử dụng async; người lập chỉ mục thuộc tính không thể sử dụng async; trong tình huống của tôi, tôi cần phải tự động đánh giá một chuỗi như "GetDefaultWelcomeMessage [\" InitialMessage \ "]" mà phương pháp này làm cho một HttpCall và cú pháp chỉ số là một lợi thế cho phương thức cú pháp "Util.GetDefaultWelcomeMessage (\" InitialMessage \ ")"
Eugen

7

Nếu bạn đang xây dựng thư viện lớp, thì có lẽ người dùng thư viện của bạn muốn sử dụng thư viện của bạn không đồng bộ. Tôi nghĩ đó là lý do lớn nhất ngay tại đó.

Bạn cũng không biết thư viện của bạn sẽ được sử dụng như thế nào. Có lẽ người dùng sẽ xử lý rất nhiều yêu cầu và thực hiện không đồng bộ sẽ giúp nó thực hiện nhanh hơn và hiệu quả hơn.

Nếu bạn có thể làm như vậy một cách đơn giản, hãy cố gắng không đặt gánh nặng lên người dùng thư viện của bạn đang cố gắng làm cho dòng chảy không đồng bộ khi bạn có thể chăm sóc nó cho họ.

Lý do duy nhất tôi không sử dụng phiên bản async là nếu tôi đang cố gắng hỗ trợ phiên bản .NET cũ hơn chưa được tích hợp hỗ trợ async.


Tôi thấy, vì vậy làm cho thư viện lớp không đồng bộ và cho phép người dùng hệ thống quyết định sử dụng nó không đồng bộ hay sử dụng chờ đợi và sử dụng đồng bộ?
Sốt cà chua

erm, await giúp thực hiện một số cuộc gọi không đồng bộ bằng cách trả lại quyền điều khiển cho người gọi.
Josh Smeaton

6

Trong trường hợp của tôi, câu trả lời được chấp nhận đã không hoạt động. Tôi đã gọi API từ một ứng dụng MVC không có hành động không đồng bộ.

Đây là cách tôi quản lý để làm cho nó hoạt động:

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();
    }

Sau đó, tôi gọi nó như thế này:

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

1
Thx @Darkonekt ... Điều này hoạt động hoàn hảo đối với tôi. Chỉ có httpClient.SendAsync (...). Kết quả không bao giờ hoạt động bên trong AspNet Handler (.ASHX).
Rafael Kazuo Sato Simiao

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();
}

Sau đó

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

nếu bạn sử dụng lớp đó truyền phương thức async của bạn làm tham số, bạn có thể gọi các phương thức async từ phương thức đồng bộ hóa một cách an toàn.

nó được giải thích ở đây: https://cpratt.co/async-tips-tricks/


-1

Tất cả các câu trả lời dường như tập trung vào việc sử dụng HttpClientđồng bộ thay vì đưa ra câu trả lời thực sự cho câu hỏi.

HttpClientkhông chỉ là một trình xử lý yêu cầu / phản hồi đơn giản để nó có thể xử lý một số đặc thù của các mạng khác nhau. Cụ thể trong trường hợp của tôi làm việc với proxy NTLM yêu cầu đàm phán, gửi nhiều yêu cầu / phản hồi với mã thông báo và thông tin xác thực giữa máy khách và máy chủ proxy để xác thực. HttpClient(sử dụng HttpClientHandler) dường như có cơ chế tích hợp xử lý trả lại tài nguyên ngoài proxy bằng một cuộc gọi phương thức.


Câu trả lời của bạn không giải thích cách sử dụng không đồng bộ httpClient.
dùng275801

@ user275801 Đó là nhận xét ngu ngốc. Không ai hỏi điều đó. Nó không đồng bộ theo mặc định.
Bizniztime
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.