การโทรแบบ Async ด้วยการรอใน HttpClient จะไม่ส่งกลับ


98

ฉันมีสายที่ฉันโทรจากภายในC#แอปพลิเคชันรถไฟใต้ดินที่ใช้xaml บน Win8 CP สายนี้เพียงแค่กระทบบริการเว็บและส่งคืนข้อมูล 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);

มันค้างที่awaitแต่การโทรhttpส่งกลับเกือบจะในทันที (ยืนยันผ่านมือไม่พาย); ราวกับว่าawaitถูกเพิกเฉยและมันก็ค้างอยู่ที่นั่น

ก่อนที่คุณจะถาม - ใช่ - ความสามารถของเครือข่ายส่วนตัวเปิดอยู่

ความคิดใด ๆ ที่จะแขวน?


1
คุณเรียกasyncวิธีนั้นว่าอย่างไร? มันไม่ทำให้เกิดข้อยกเว้น?
svick

คำตอบ:


140

ลองดูคำตอบสำหรับคำถามของฉันซึ่งดูเหมือนจะคล้ายกันมาก

บางสิ่งบางอย่างที่จะลอง: โทรในงานกลับโดยConfigureAwait(false) GetStreamAsync()เช่น

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

สิ่งนี้จะมีประโยชน์หรือไม่นั้นขึ้นอยู่กับการเรียกรหัสของคุณข้างต้น - ในกรณีของฉันเรียกasyncเมธอดโดยใช้Task.GetAwaiter().GetResult()ทำให้รหัสค้าง

เนื่องจากGetResult()บล็อกเธรดปัจจุบันจนกว่างานจะเสร็จสมบูรณ์ เมื่องานเสร็จสิ้นจะพยายามป้อนบริบทเธรดที่เริ่มต้นใหม่อีกครั้ง แต่ทำไม่ได้เนื่องจากมีเธรดอยู่แล้วในบริบทนั้นซึ่งถูกบล็อกโดยการเรียกไปยังGetResult()... การหยุดชะงัก!

โพสต์ MSDN นี้มีรายละเอียดเล็กน้อยเกี่ยวกับวิธีที่. NET ซิงโครไนซ์เธรดแบบขนาน - และคำตอบที่ให้กับคำถามของฉันเองจะให้แนวทางปฏิบัติที่ดีที่สุด


12
ขอบคุณเกือบจะยอมแพ้ async / รอก่อนที่จะเห็นสิ่งนี้
Den

4
ฉันด้วย! เหตุใดสิ่งนี้จึงไม่ได้รับการจัดทำเป็นเอกสารที่ดีกว่า ขอบคุณอีกครั้ง
Avrohom Yisroel

1
จะเกิดขึ้นหรือไม่ถ้าไม่อยู่ในบริบท UI และบริบท ASP.NET
machinarium

1
คำตอบสุดเจ๋ง! แต่ฉันสับสนว่าทำไมจนถึงตอนนี้ฉันมีปัญหานี้เมื่อใช้ HttpClient ดูเหมือนว่าการใช้งานพื้นฐานภายใน HttpClient จะไม่ถูกนำไปใช้อย่างถูกต้อง วิธีแก้ปัญหาอื่น ๆ ที่ฉันพบเกี่ยวข้องกับการตั้งค่าเธรดปัจจุบันเป็น STA ซึ่งช่วยได้ แต่เป็นทางอ้อมโดยเฉพาะอย่างยิ่งเมื่อคุณใช้แอสเซมบลีของบุคคลที่สามและไม่ทราบว่าภายใต้ประทุนบางสายกำลังรอการตอบกลับอย่างแน่นอน จะไม่มีวันได้รับ ในกรณีของฉัน dll อยู่ในบ้านดังนั้นเราจึงสามารถ ConfigureAwait ... แต่ต้องทำในระดับต่ำสุดถึง HttpClient obj
Chris Schaller

2
@ChrisSchaller อย่าลืมอ่านคำตอบแบบเต็มที่stackoverflow.com/a/10351400/174735ซึ่งอธิบายปัญหาได้ค่อนข้างครบถ้วน
Benjamin Fox

5

เพียงแค่แจ้งให้ทราบล่วงหน้า - หากคุณพลาดการรอคอยที่ระดับบนสุดในตัวควบคุม ASP.NET และคุณส่งคืนงานแทนที่จะเป็นผลลัพธ์เป็นการตอบกลับจริงๆแล้วมันก็แฮงค์ในการรอสายที่ซ้อนกันโดยไม่มีข้อผิดพลาด ความผิดพลาดโง่ ๆ แต่ถ้าฉันเห็นโพสต์นี้อาจช่วยให้ฉันประหยัดเวลาในการตรวจสอบโค้ดเพื่อหาสิ่งแปลก ๆ


0

คำเตือน: ฉันไม่ชอบโซลูชัน ConfigureAwait () เพราะฉันคิดว่ามันไม่ใช้งานง่ายและจำยาก แต่ฉันได้ข้อสรุปเพื่อตัดการเรียกเมธอดที่ไม่ได้รอใน Task.Run (() => myAsyncMethodNotUsingAwait ()) ดูเหมือนจะใช้งานได้ 100% แต่อาจเป็นเพียงสภาพการแข่งขัน!? ฉันไม่แน่ใจว่าเกิดอะไรขึ้นกับความซื่อสัตย์ ข้อสรุปนี้อาจผิดพลาดและฉันเสี่ยงต่อ StackOverflow ของฉันที่นี่เพื่อหวังว่าจะได้เรียนรู้จากความคิดเห็น :-P โปรดอ่าน!

ฉันเพิ่งมีปัญหาตามที่อธิบายไว้และพบข้อมูลเพิ่มเติมที่นี่

คำสั่งคือ: "คุณไม่สามารถเรียกวิธีการอะซิงโครนัส"

await asyncmethod2()

จากวิธีการที่บล็อก

myAsyncMethod().Result

ในกรณีของฉันฉันไม่สามารถเปลี่ยนวิธีการโทรได้และมันไม่ได้เป็นแบบ async แต่ฉันไม่ได้สนใจเกี่ยวกับผลลัพธ์จริงๆ อย่างที่ฉันจำได้ว่ามันไม่ได้ผลเช่นกันการลบ. ผลลัพธ์และทำให้การรอคอยที่ขาดหายไป

ฉันจึงทำสิ่งนี้:

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

ในกรณีของฉันฉันไม่สนใจเกี่ยวกับผลลัพธ์ในวิธีการเรียกที่ไม่ใช่ async แต่ฉันเดาว่าเป็นเรื่องธรรมดาในกรณีการใช้งานนี้ คุณสามารถใช้ผลลัพธ์ในวิธีการเรียก async

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.