แนวทางปฏิบัติที่ดีที่สุดสำหรับการใช้ Multithreading ใน .NET: การใช้ Async IO เพื่อเพิ่มประสิทธิภาพ
การใช้งานหลายเธรด (Multithreading) เป็นเครื่องมือที่มีประโยชน์มากสำหรับการปรับแต่งประสิทธิภาพ โดยเฉพาะในแอปพลิเคชันที่ต้องการทำงานหลายอย่างพร้อมกัน เช่น การดึงข้อมูลจากฐานข้อมูลและทำการร้องขอหลายบริการเว็บ อย่างไรก็ตาม ไม่มีกลยุทธ์ในการใช้หลายเธรดที่มีความเหมือนกัน ในบล็อกโพสต์นี้ เราจะสำรวจสถานการณ์ทั่วไปสำหรับการใช้หลายเธรดใน .NET และแนะนำคุณให้รู้จักกับแนวทางปฏิบัติที่ดีที่สุดที่จะช่วยปรับปรุงประสิทธิภาพของแอปพลิเคชันของคุณอย่างมาก: Async IO
ปัญหา: คำร้องขอบริการเว็บหลายรายการ
ลองจินตนาการว่าคุณมีโปรแกรมที่ออกแบบให้ดึงข้อมูล 100 รายการจากฐานข้อมูล และสำหรับแต่ละรายการนั้น จำเป็นต้องได้รับข้อมูลอัปเดตจากบริการเว็บ เพื่อให้การประมวลผลแบบขนาน (Parallelism) มีอยู่สองทางเลือกที่มักจะพิจารณา:
-
การเปิดเธรดใหม่สำหรับทุกคำร้องขอ: หนึ่งในแนวทางคือการเริ่มร้องขอบริการเว็บในเธรดใหม่ คุณสามารถควบคุมจำนวนเธรดที่ทำงานพร้อมกันได้ทั้งจากพารามิเตอร์ภายนอกหรือตามที่โหลด
-
การประมวลผลแบบกลุ่มด้วยเธรดที่น้อยกว่า: อีกแนวทางหนึ่งคือการสร้างกลุ่มขนาดเล็ก เช่นกลุ่มละ 10 รายการ และเปิดแต่ละกลุ่มในเธรดแยก ทำให้มีเธรดพร้อมกันประมาณ 10 เธรด
แม้ว่าวิธีทั้งสองมีความเหมาะสม แต่สามารถนำไปสู่ความซับซ้อนและไม่มีประสิทธิภาพได้
แนวทางที่ดีที่สุด: การใช้ Async IO
หลังจากวิเคราะห์ตัวเลือกต่างๆ ผู้ชนะที่ชัดเจนคือ ตัวเลือก 3: การใช้ Async IO
แนวทางนี้มีความมีประสิทธิภาพโดยเฉพาะ เนื่องจากมันใช้ประโยชน์จากความจริงที่ว่าโปรแกรมของคุณจะใช้เวลาส่วนใหญ่ในการรอคำตอบจากการร้องขอ HTTP นี่คือภาพรวมโดยละเอียดว่าทำไม Async IO จึงมีข้อดีมากมายและวิธีการนำไปใช้ในแอปพลิเคชัน .NET ของคุณ
ทำไมต้องเลือก Async IO?
-
ประสิทธิภาพ: โดยการใช้การเรียกแบบอสมการ แอปพลิเคชันของคุณจะหลีกเลี่ยงภาระทางการจัดการเธรดที่ซับซ้อน นั่นหมายถึงการใช้ทรัพยากรที่น้อยลงและประสิทธิภาพที่ดีขึ้น
-
ความเรียบง่าย: การใช้เธรดเดียวในการจัดการคำร้องขออสมการทำให้โค้ดเรียบง่ายมากขึ้น โดยลดจำนวนการซิงโครไนซ์ที่จำเป็น คุณไม่ต้องกังวลเกี่ยวกับความซับซ้อนของการล็อคทรัพยากรร่วมในหลายเธรด
-
การจัดการการรอ: สแต็คเน็ตเวิร์คของ Windows (หรือ .NET Framework) จัดการรอกระบวนการตอบสนองให้คุณ ทำให้คุณสามารถหลีกเลี่ยงความยุ่งยากเกี่ยวกับการจัดการเธรด
การนำ Async IO ไปใช้ใน C#
นี่คือตัวอย่างพื้นฐานของการนำ Async IO ไปใช้ในแอปพลิเคชัน .NET ของคุณเพื่อดึงข้อมูลการตอบสนองจากบริการเว็บ:
using System.Net; // เนมสเปซที่จำเป็น
// คลาส State เพื่อเก็บข้อมูลการร้องขอ
class RequestState {
public WebRequest Request { get; set; }
}
static void Main(string[] args) {
// สร้างคำร้องขอบริการเว็บ
HttpWebRequest request = WebRequest.Create("http://www.stackoverflow.com") as HttpWebRequest;
request.BeginGetResponse(
(asyncResult) => {
// ดึงวัตถุคำร้องขอเมื่อเสร็จสิ้น
var state = (RequestState)asyncResult.AsyncState;
var webResponse = state.Request.EndGetResponse(asyncResult) as HttpWebResponse;
// ตรวจสอบว่าตอบสนองสำเร็จ
Debug.Assert(webResponse.StatusCode == HttpStatusCode.OK);
Console.WriteLine("ได้รับการตอบสนองจากเซิร์ฟเวอร์: " + webResponse.Server);
},
new RequestState { Request = request }
);
Console.WriteLine("รอการตอบสนอง กดปุ่มใด ๆ เพื่อออก");
Console.ReadKey();
}
หมายเหตุสำคัญ
- ฟังก์ชันคืนค่าที่เสร็จสิ้นสำหรับคำร้องขออสมการจะถูกดำเนินการในเธรด ThreadPool ไม่ใช่เธรดหลัก
- ควรจัดการทรัพยากรที่ใช้ร่วมกันด้วยล็อคเพื่อป้องกันความเสียหายของข้อมูล
บทสรุป
ในแวดวงการออกแบบหลายเธรด การใช้ Async IO
ในแอปพลิเคชัน .NET เป็นแนวทางปฏิบัติที่ดีที่สุดสำหรับการจัดการคำร้องขอบริการเว็บพร้อมกันอย่างมีประสิทธิภาพ แทนที่จะต้องจัดการเธรดหลายตัว คุณสามารถใช้ความสามารถของอสมการใน .NET เพื่อสร้างแอปพลิเคชันที่ตอบสนองและมีประสิทธิภาพสูงสุดในขณะที่ลดความซับซ้อนให้น้อยที่สุด
โดยการนำแนวปฏิบัติเหล่านี้ไปใช้ คุณจะไม่เพียงแต่เพิ่มขนาดที่สามารถรองรับได้ของแอปพลิเคชันของคุณ แต่ยังทำให้กระบวนการพัฒนาของคุณน่าสนุกมากขึ้นและมีโอกาสผิดพลาดน้อยลงอีกด้วย ขอให้สนุกกับการเขียนโค้ด!