Panggilan asinkron dengan await di HttpClient tidak pernah kembali

95

Saya mendapat panggilan yang saya lakukan dari dalam C#aplikasi metro berbasis xaml di CP Win8; panggilan ini hanya mengenai layanan web dan mengembalikan data JSON.

HttpMessageHandler handler = new HttpClientHandler();

HttpClient httpClient = new HttpClient(handler);
httpClient.BaseAddress = new Uri("http://192.168.1.101/api/");

var result = await httpClient.GetStreamAsync("weeklyplan");
DataContractJsonSerializer ser = new DataContractJsonSerializer(typeof(WeeklyPlanData[]));
return (WeeklyPlanData[])ser.ReadObject(result);

Itu hang di awaittapi panggilan http benar-benar kembali segera (dikonfirmasi melalui fiddler); seolah-olah awaitdiabaikan dan hanya tergantung di sana.

Sebelum Anda bertanya - YA - kapabilitas Jaringan Pribadi telah diaktifkan.

Ada ide mengapa ini akan menggantung?

keithwarren7
sumber
1
Bagaimana Anda menyebut asyncmetode itu? Bukankah itu membuat pengecualian?
svick

Jawaban:

136

Lihat jawaban atas pertanyaan saya ini yang tampaknya sangat mirip.

Sesuatu untuk dicoba: panggil ConfigureAwait(false)Tugas yang dikembalikan oleh GetStreamAsync(). Misalnya

var result = await httpClient.GetStreamAsync("weeklyplan")
                             .ConfigureAwait(continueOnCapturedContext:false);

Apakah ini berguna atau tidak tergantung pada bagaimana kode Anda di atas dipanggil - dalam kasus saya memanggil asyncmetode menggunakan Task.GetAwaiter().GetResult()menyebabkan kode hang.

Ini karena GetResult()memblokir utas saat ini hingga Tugas selesai. Ketika tugas selesai, ia mencoba untuk masuk kembali ke konteks utas tempat ia dimulai tetapi tidak bisa karena sudah ada utas dalam konteks itu, yang diblokir oleh panggilan ke GetResult()... deadlock!

Posting MSDN ini membahas sedikit detail tentang bagaimana .NET menyinkronkan utas paralel - dan jawaban yang diberikan untuk pertanyaan saya sendiri memberikan beberapa praktik terbaik.

Benjamin Fox
sumber
12
Terima kasih, hampir menyerah pada async / menunggu sebelum melihat ini.
Den
4
Saya juga! Mengapa hal ini tidak didokumentasikan dengan lebih baik? Sekali lagi terima kasih
Avrohom Yisroel
1
Apakah itu akan terjadi jika tidak ada dalam konteks UI dan konteks ASP.NET?
machinarium
1
Jawaban yang luar biasa! Tetapi saya bingung mengapa sejauh ini saya hanya mengalami masalah ini saat menggunakan HttpClient, sepertinya implementasi yang mendasari dalam HttpClient tidak diimplementasikan dengan benar. Solusi lain yang saya temukan melibatkan pengaturan utas saat ini sebagai STA yang membantu tetapi benar-benar tidak langsung terutama ketika Anda menggunakan rakitan pihak ke-3 dan tidak menyadari bahwa di bawah tenda beberapa panggilan akan menunggu pasti untuk tanggapan bahwa itu tidak akan pernah mendapatkan. Dalam kasus saya, dll itu ada di rumah, jadi kami dapat ConfigureAwait ... tetapi itu harus dilakukan pada tingkat terendah ke objek HttpClient.
Chris Schaller
2
@ChrisSchaller Pastikan untuk membaca jawaban lengkapnya di stackoverflow.com/a/10351400/174735 , yang menjelaskan masalah dengan cukup lengkap.
Benjamin Fox
5

Sekadar pemberitahuan - jika Anda melewatkan menunggu di tingkat atas dalam pengontrol ASP.NET, dan Anda mengembalikan tugas alih-alih hasilnya sebagai respons, itu sebenarnya hanya hang di panggilan tunggu bersarang tanpa kesalahan. Kesalahan konyol, tetapi jika saya melihat posting ini, itu mungkin telah menghemat waktu saya memeriksa kode untuk sesuatu yang aneh.

mendidih
sumber
0

Penafian: Saya tidak suka solusi ConfigureAwait () karena menurut saya solusi ini tidak intuitif dan sulit untuk diingat. Sebagai gantinya saya sampai pada kesimpulan untuk membungkus panggilan metode yang tidak ditunggu di Task.Run (() => myAsyncMethodNotUsingAwait ()). Ini sepertinya berhasil 100% tetapi mungkin hanya kondisi balapan !? Saya tidak begitu yakin apa yang sebenarnya terjadi. Kesimpulan ini mungkin salah dan saya mempertaruhkan poin StackOverflow saya di sini untuk belajar dari komentar :-P. Mohon dibaca!

Saya baru saja mengalami masalah seperti yang dijelaskan dan menemukan info selengkapnya di sini .

Pernyataannya adalah: "Anda tidak dapat memanggil metode asinkron"

await asyncmethod2()

dari metode yang memblokir

myAsyncMethod().Result

Dalam kasus saya, saya tidak dapat mengubah metode panggilan dan itu tidak asinkron. Tapi saya sebenarnya tidak peduli dengan hasilnya. Seperti yang saya ingat, itu juga tidak berhasil menghapus .Result dan menunggu hilang.

Jadi saya melakukan ini:

public void Configure()
{
    var data = "my data";
    Task.Run(() => NotifyApi(data));
}

private async Task NotifyApi(bool data)
{
    var toSend = new StringContent(JsonConvert.SerializeObject(data), Encoding.UTF8, "application/json");
    await client.PostAsync("http://...", data);
}

Dalam kasus saya, saya tidak peduli dengan hasil dalam memanggil metode non-async tetapi saya rasa itu cukup umum dalam kasus penggunaan ini. Anda dapat menggunakan hasilnya dalam metode pemanggilan async.

CodingYourLife
sumber