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.

Saus tomat
sumber
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.

Darin Dimitrov
sumber
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.

trev
sumber
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.

Josh Smeaton
sumber
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)));
Jonathan Alfaro
sumber
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/

Lean Bonaventura
sumber
-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.

Waktu Bizniz
sumber
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