Mengapa menggunakan HttpClient untuk Koneksi Sinkron


189

Saya sedang membangun perpustakaan kelas untuk berinteraksi dengan API. Saya perlu memanggil API dan memproses respons XML. Saya dapat melihat manfaat dari menggunakan HttpClientuntuk konektivitas Asynchronous, tetapi apa yang saya lakukan adalah murni sinkron, jadi saya tidak dapat melihat manfaat signifikan dari penggunaan HttpWebRequest.

Jika ada yang bisa menjelaskan, saya akan sangat menghargainya. Saya bukan orang yang menggunakan teknologi baru untuk itu.


3
Saya benci memberi tahu Anda, tetapi panggilan melalui HTTP tidak pernah murni sinkron karena cara kerja jaringan windows (alias port penyelesaian).
TomTom


Jawaban:


374

tetapi apa yang saya lakukan adalah murni sinkron

Anda dapat menggunakan HttpClientuntuk permintaan sinkron:

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

Sejauh mengapa Anda harus menggunakan HttpClientlebih dari WebRequestyang bersangkutan, yah, HttpClientadalah anak baru di blok dan bisa berisi perbaikan lebih dari klien lama.


27
Tidakkah penggunaan metode async yang sinkron berpotensi memblokir utas UI Anda? Anda mungkin ingin mempertimbangkan sesuatu seperti string responseString = Task.Run(() => responseContent.ReadAsStringAsync()).Result;jika Anda harus membuat ini sinkron.
penduduk bumi

13
@earthling, ya, Task.Runmenjalankan tugas dari ThreadPool, tetapi Anda memintanya .Resultuntuk membunuh semua manfaat dari ini dan memblokir utas yang Anda panggil ini .Result(yang biasanya merupakan utas UI utama).
Darin Dimitrov

35
Menurut posting ini ( blogs.msdn.com/b/pfxteam/archive/2012/04/13/10293638.aspx ) panggilan .Resultseperti ini dapat menghabiskan threadpool dan menyebabkan kebuntuan.
Pete Garafano

16
Kode ini akan selalu menemui jalan buntu jika dieksekusi dalam tugas yang dibuat pada utas UI utama olehnew TaskFactory(TaskScheduler.FromCurrentSynchronizationContext()
Wim Coenen

24
Jadi, bagaimana cara menggunakan HttpClient secara sinkron dari utas UI? Katakanlah saya sengaja ingin memblokir utas UI (atau saya sedang menulis aplikasi konsol) sampai saya mendapatkan respons HTTP ... Jadi, jika Tunggu (), Hasil (), dll dapat menyebabkan kebuntuan, apa solusi yang pasti untuk ini tanpa risiko kebuntuan dan tanpa menggunakan kelas lain seperti WebClient?
Dexter

26

Saya akan mengulangi jawaban Donny V. dan Josh

"Satu-satunya alasan saya tidak akan menggunakan versi async adalah jika saya mencoba untuk mendukung versi yang lebih lama dari .NET yang belum memiliki dukungan async."

(dan tersanjung jika saya memiliki reputasi.)

Saya tidak dapat mengingat kapan terakhir kali, saya bersyukur atas fakta bahwa HttpWebRequest melemparkan pengecualian untuk kode status> = 400. Untuk mengatasi masalah ini, Anda perlu segera menangkap pengecualian, dan memetakannya ke beberapa mekanisme respons non-pengecualian dalam kode Anda ... membosankan, membosankan dan rawan kesalahan itu sendiri. Apakah itu berkomunikasi dengan database, atau menerapkan proxy web yang dipesan lebih dahulu, 'hampir' selalu diinginkan bahwa driver Http hanya memberi tahu kode aplikasi Anda apa yang dikembalikan, dan menyerahkan kepada Anda untuk memutuskan bagaimana berperilaku.

Karenanya HttpClient lebih disukai.


1
Saya terkejut bahwa HttpClientitu sendiri adalah pembungkus HttpWebRequest(yang memang, secara internal menangkap WebExceptionobjek - objek dan melakukan konversi HttpResponseMessageuntuk Anda). Saya akan berpikir akan lebih mudah untuk membangun klien baru sepenuhnya dari awal.
Dai

4
Ada banyak alasan bagus seperti tidak ingin menulis ulang seluruh basis kode Anda hanya untuk panggilan http tingkat sangat rendah yang bahkan kinerja tidak kritis (tetapi akan memperkenalkan async ke sejuta tempat).
FrankyBoy

Di .net core 2 jika Anda ingin secara dinamis mengevaluasi ekspresi dengan DynamicExpressionParser, mungkin tidak mungkin menggunakan async; pengindeks properti tidak dapat menggunakan async; dalam situasi saya, saya perlu mengevaluasi string secara dinamis seperti "GetDefaultWelcomeMessage [\" InitialMessage \ "]" di mana metode ini membuat HttpCall dan sintaks indeks lebih disukai daripada sintaks metode "Util.GetDefaultWelcomeMessage (\" InitialMessage \ ")"
eugen

7

Jika Anda sedang membangun perpustakaan kelas, maka mungkin pengguna perpustakaan Anda ingin menggunakan perpustakaan Anda secara tidak sinkron. Saya pikir itu alasan terbesar di sana.

Anda juga tidak tahu bagaimana perpustakaan Anda akan digunakan. Mungkin pengguna akan memproses banyak dan banyak permintaan, dan melakukannya secara tidak sinkron akan membantu kinerjanya lebih cepat dan lebih efisien.

Jika Anda dapat melakukannya dengan sederhana, cobalah untuk tidak membebani pengguna perpustakaan Anda mencoba membuat aliran asinkron ketika Anda bisa mengurusnya untuk mereka.

Satu-satunya alasan saya tidak akan menggunakan versi async adalah jika saya mencoba untuk mendukung versi .NET yang lebih lama yang belum memiliki dukungan async.


Saya mengerti, jadi buat perpustakaan kelas asinkron, dan izinkan pengguna sistem untuk memutuskan apakah akan menggunakannya async, atau untuk menggunakan menunggu dan menggunakannya secara serempak?
Ketchup

erm, menanti membantu membuat panggilan tertentu tidak sinkron dengan mengembalikan kontrol ke pemanggil.
Josh Smeaton

6

Dalam kasus saya, jawaban yang diterima tidak berhasil. Saya menelepon API dari aplikasi MVC yang tidak memiliki tindakan async.

Beginilah cara saya membuatnya berfungsi:

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

Lalu saya menyebutnya seperti ini:

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

1
Thx @Darkonekt ... Ini berfungsi dengan baik untuk saya. Hanya HttpClient.SendAsync (...). Hasilnya tidak pernah berfungsi di dalam 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();
}

Kemudian

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

jika Anda menggunakan kelas itu melewati metode async Anda sebagai parameter Anda dapat memanggil metode async dari metode sinkronisasi dengan cara yang aman.

dijelaskan di sini: https://cpratt.co/async-tips-tricks/


-1

Semua jawaban tampaknya berfokus pada penggunaan secara HttpClientsinkron alih-alih memberikan jawaban aktual untuk pertanyaan.

HttpClientlebih dari sekadar penangan request / response sehingga dapat menangani beberapa kekhasan jaringan yang berbeda. Yaitu dalam kasus saya bekerja dengan proksi NTLM yang membutuhkan negosiasi, mengirim beberapa permintaan / tanggapan dengan token dan kredensial antara klien dan server proxy untuk mengotentikasi. HttpClient(Menggunakan HttpClientHandler) tampaknya memiliki mekanisme bawaan yang menangani yang mengembalikan sumber daya di luar proxy dengan satu pemanggilan metode.


Jawaban Anda juga tidak menjelaskan cara menggunakan HttpClient secara tidak sinkron.
user275801

@ user275801 Itu komentar bodoh. Tidak ada yang menanyakan itu. Asinkron secara default.
Bizniztime
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.