HttpClient SendAsync บล็อกเธรดหลัก
ฉันได้เขียนแอปพลิเคชั่น winforms เล็กน้อยที่ส่งคำขอ http ไปยังที่อยู่ IP ทุกเครื่องภายในเครือข่ายท้องถิ่นของฉันเพื่อค้นหาอุปกรณ์บางอย่างของฉัน บนซับเน็ตมาสก์เฉพาะของฉันมี 512 ที่อยู่ ฉันเขียนสิ่งนี้โดยใช้ backGroundWorker แต่ฉันต้องการลองใช้ httpClient และรูปแบบ Async / Await เพื่อให้ได้สิ่งเดียวกัน โค้ดด้านล่างใช้ httpClient เพียงอินสแตนซ์เดียวและฉันรอจนกว่าคำขอทั้งหมดจะเสร็จสมบูรณ์ ปัญหานี้คือเธรดหลักถูกบล็อก ฉันรู้สิ่งนี้เพราะฉันมี picturebox + กำลังโหลด gif และมันไม่เคลื่อนไหวอย่างสม่ำเสมอ ฉันใส่เมธอด GetAsync ใน Task Run ตามที่แนะนำที่นี่แต่ก็ไม่ได้ผลเช่นกัน
private async void button1_Click(object sender, EventArgs e)
{
var addresses = networkUtils.generateIPRange..
await MakeMultipleHttpRequests(addresses);
}
public async Task MakeMultipleHttpRequests(IPAddress[] addresses)
{
List<Task<HttpResponseMessage>> httpTasks = new List<Task<HttpResponseMessage>>();
foreach (var address in addresses)
{
Task<HttpResponseMessage> response = MakeHttpGetRequest(address.ToString());
httpTasks.Add(response);
}
try
{
if (httpTasks.ToArray().Length != 0)
{
await Task.WhenAll(httpTasks.ToArray());
}
}
catch (Exception ex)
{
Console.WriteLine("\thttp tasks did not complete Exception : {0}", ex.Message);
}
}
private async Task<HttpResponseMessage> MakeHttpGetRequest(string address)
{
var url = string.Format("http://{0}/getStatus", address);
var cts = new System.Threading.CancellationTokenSource();
cts.CancelAfter(TimeSpan.FromSeconds(10));
HttpResponseMessage response = null;
var request = new HttpRequestMessage(HttpMethod.Get, url);
response = await httpClient.SendAsync(request, cts.Token);
return response;
}
ฉันได้อ่านปัญหาที่คล้ายกันที่นี่แต่เธรด gui ของฉันไม่ค่อยมีประโยชน์ ฉันได้อ่านที่นี่ว่าฉันอาจจะหมดกระทู้ นี่คือปัญหาฉันจะแก้ไขได้อย่างไร ฉันรู้ว่ามันคือ Send Async เพราะถ้าฉันแทนที่โค้ดด้วยงานง่ายๆด้านล่างจะไม่มีการปิดกั้น
await Task.Run(() =>
{
Thread.Sleep(1000);
});
คำตอบ
ดังนั้นปัญหาอย่างหนึ่งที่นี่คือคุณกำลังสร้างงานมากกว่า 500 งานต่อเนื่องกันอย่างรวดเร็วโดยมีการตั้งค่าการหมดเวลานอกการสร้างงาน
เพียงเพราะคุณขอให้ทำงานมากกว่า 500 งานไม่ได้หมายความว่างานมากกว่า 500 งานจะทำงานในเวลาเดียวกัน พวกเขาเข้าคิวและทำงานเมื่อตัวกำหนดตารางเวลาเห็นว่าเป็นไปได้
คุณตั้งค่าการหมดเวลา ณ เวลาที่สร้าง 10 วินาที แต่พวกเขาสามารถนั่งในตารางเวลา 10 วินาทีก่อนที่จะดำเนินการได้
คุณต้องการให้คำขอ Http ของคุณหมดเวลาโดยอัตโนมัติคุณสามารถทำได้เช่นนี้เมื่อคุณสร้างHttpClient:
private static readonly HttpClient _httpClient = new HttpClient
{
Timeout = TimeSpan.FromSeconds(10)
};
ดังนั้นเมื่อเลื่อนระยะหมดเวลาไปที่HttpClientเมธอดของคุณตอนนี้ควรมีลักษณะดังนี้:
private static Task<HttpResponseMessage> MakeHttpGetRequest(string address)
{
return _httpClient.SendAsync(new HttpRequestMessage(HttpMethod.Get, new UriBuilder
{
Host = address,
Path = "getStatus"
}.Uri));
}
ลองใช้วิธีนั้นและดูว่าจะช่วยปรับปรุงปัญหาการล็อกในโหมดดีบักหรือไม่
เท่าที่คุณพบปัญหา: มันถูกล็อกเนื่องจากคุณอยู่ในโหมดดีบักและดีบักเกอร์พยายามที่จะพูดว่า "เฮ้คุณมีข้อยกเว้น" 500 ครั้งในเวลาเดียวกันเพราะพวกมันเกิดพร้อมกันทั้งหมด เรียกใช้ในโหมดรีลีสและดูว่ายังล็อกอยู่หรือไม่
สิ่งที่ฉันจะพิจารณาคือการจัดกลุ่มการดำเนินงานของคุณ ทำ 20 จากนั้นรอจนกว่า 20 เหล่านั้นจะเสร็จสิ้นทำอีก 20 อย่างไปเรื่อย ๆ
หากคุณต้องการเห็นวิธีการจัดชุดงานที่ราบรื่นโปรดแจ้งให้เราทราบเรายินดีเป็นอย่างยิ่งที่จะแสดงให้คุณเห็น
เมื่อวันที่ .NET Framework จำนวนการเชื่อมต่อไปยังเซิร์ฟเวอร์ที่ถูกควบคุมโดยระดับ ServicePointManager
สำหรับไคลเอ็นต์ขีด จำกัด การเชื่อมต่อเริ่มต้นคือ2ในกระบวนการไคลเอ็นต์
ไม่ว่าคุณจะเรียกใช้ HttpClient.SendAsync กี่ครั้งระบบจะเปิดใช้งาน 2 รายการพร้อมกันเท่านั้น
แต่คุณสามารถจัดการการเชื่อมต่อด้วยตัวคุณเอง
บน. NET Core นี่ไม่ใช่แนวคิดของตัวจัดการจุดบริการและขีด จำกัด เริ่มต้นที่เทียบเท่ากันคือ int.MaxValue