HttpClient SendAsync บล็อกเธรดหลัก

Aug 21 2020

ฉันได้เขียนแอปพลิเคชั่น 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);
    });

คำตอบ

2 Andy Aug 21 2020 at 05:31

ดังนั้นปัญหาอย่างหนึ่งที่นี่คือคุณกำลังสร้างงานมากกว่า 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 อย่างไปเรื่อย ๆ

หากคุณต้องการเห็นวิธีการจัดชุดงานที่ราบรื่นโปรดแจ้งให้เราทราบเรายินดีเป็นอย่างยิ่งที่จะแสดงให้คุณเห็น

1 PauloMorgado Aug 21 2020 at 17:03

เมื่อวันที่ .NET Framework จำนวนการเชื่อมต่อไปยังเซิร์ฟเวอร์ที่ถูกควบคุมโดยระดับ ServicePointManager

สำหรับไคลเอ็นต์ขีด จำกัด การเชื่อมต่อเริ่มต้นคือ2ในกระบวนการไคลเอ็นต์

ไม่ว่าคุณจะเรียกใช้ HttpClient.SendAsync กี่ครั้งระบบจะเปิดใช้งาน 2 รายการพร้อมกันเท่านั้น

แต่คุณสามารถจัดการการเชื่อมต่อด้วยตัวคุณเอง

บน. NET Core นี่ไม่ใช่แนวคิดของตัวจัดการจุดบริการและขีด จำกัด เริ่มต้นที่เทียบเท่ากันคือ int.MaxValue