นอกเหนือจาก / สำหรับงาน I/O แล้ว .NET ยังมี สำหรับ — การรันการคำนวณกระจายไปหลายคอร์ ความแตกต่างสำคัญคือ: / มีไว้สำหรับ I/O concurrency (การรอแบบไม่บล็อก) ขณะที่ , และ PLINQ มีไว้สำหรับการ parallelize งานที่ใช้ CPU หนัก
นอกเหนือจาก / สำหรับงาน I/O แล้ว .NET ยังมี สำหรับ — การรันการคำนวณกระจายไปหลายคอร์ ความแตกต่างสำคัญคือ: / มีไว้สำหรับ I/O concurrency (การรอแบบไม่บล็อก) ขณะที่ , และ PLINQ มีไว้สำหรับการ parallelize งานที่ใช้ CPU หนัก
asyncawaitasyncawaitTask.RunParallelasync/await → I/O-bound concurrency. Frees the thread during waits (DB, network).
Does NOT add CPU parallelism.
Parallelism → CPU-bound work spread across MULTIPLE CORES (computation, processing).
Use Task.Run, Parallel.For/ForEach, PLINQ.
// run a CPU-intensive computation on a thread pool thread (don't block the caller)
int result = await Task.Run(() => ExpensiveComputation());
// run multiple in parallel and combine
var tasks = items.Select(item => Task.Run(() => Process(item)));
var results = await Task.WhenAll(tasks); // wait for all
Task.Run จัดตารางงานบน thread ของ thread pool — ใช้มันเพื่อ parallelize การคำนวณแบบ CPU-bound (ไม่ใช่สำหรับ I/O ซึ่ง async/await ก็เพียงพอแล้วโดยไม่ต้องใช้ thread เพิ่ม)
// Parallel.For/ForEach — process a collection across cores
Parallel.ForEach(items, item => Process(item)); // automatically uses multiple cores
Parallel.For(0, 1000, i => Compute(i));
// PLINQ — parallel LINQ
var results = data.AsParallel().Where(x => IsValid(x)).Select(Transform).ToList();
Parallel.For/ForEach และ PLINQ (.AsParallel()) กระจายงานไปยังคอร์ต่าง ๆ โดยอัตโนมัติ — เหมาะอย่างยิ่งสำหรับการประมวลผลที่ใช้ CPU หนักของรายการที่เป็นอิสระต่อกัน (คุ้มค่าเฉพาะกับงานที่ใช้ CPU จริง ๆ และมีขนาดพอสมควรเท่านั้น — เพราะมี overhead)
// parallel code accessing SHARED state needs synchronization or thread-safe types
lock (_lock) { _counter++; } // lock for mutual exclusion
Interlocked.Increment(ref _counter); // lock-free atomic
var dict = new ConcurrentDictionary<string, int>(); // thread-safe collection
การเข้าใจ Task และ parallelism นอกเหนือจาก async/await เป็นความรู้ระดับ senior ที่สำคัญต่อการสร้างแอปพลิเคชัน C# ที่มีประสิทธิภาพ และ ความแตกต่างสำคัญระหว่าง I/O concurrency กับ CPU parallelism คือจุดสำคัญที่สุด — เป็นจุดที่สับสนกันบ่อย async/await จัดการ I/O-bound concurrency (ปล่อย thread ระหว่างการรอ) แต่ ไม่ เพิ่ม CPU parallelism ขณะที่งานแบบ CPU-bound (การคำนวณ, การประมวลผลข้อมูล) ได้ประโยชน์จาก parallelism จริงข้ามคอร์ผ่าน Task.Run (โอนงาน CPU ไปยัง thread ของ thread pool), Parallel.For/ForEach และ PLINQ (.AsParallel())
การรู้ว่าเครื่องมือใดเหมาะกับงานใด — async/await สำหรับ I/O, เครื่องมือ parallelism ของ TPL สำหรับงาน CPU-bound — เป็นสิ่งจำเป็นต่อการปรับปรุงประสิทธิภาพอย่างมีประสิทธิผล เพราะการใช้ผิดตัว (เช่น คาดหวังให้ async เร่งความเร็วการคำนวณ หรือสร้าง thread สำหรับ I/O ที่ async จัดการได้อย่างมีประสิทธิภาพอยู่แล้ว) ไม่ได้ผล
ที่สำคัญพอ ๆ กันคือการเข้าใจ thread safety: โค้ดแบบ parallel ที่เข้าถึงสถานะที่แชร์กันต้องการ synchronization (lock, Interlocked สำหรับ atomic operation หรือ thread-safe collection เช่น ConcurrentDictionary) เพื่อหลีกเลี่ยง race condition — เป็นความถูกต้องที่สำคัญยิ่งในโค้ดแบบ parallel
การเข้าใจความแตกต่างระหว่าง I/O กับ CPU, เครื่องมือ parallelism (Task.Run, Parallel, PLINQ) และข้อกำหนดด้าน thread-safety มีคุณค่าต่อการเขียน C# แบบ concurrent และ parallel ที่ถูกต้องและมีประสิทธิภาพ
เนื่องจากแอปพลิเคชันสมัยใหม่มักต้องการทั้ง I/O concurrency และ CPU parallelism และเนื่องจากการเลือกแนวทางที่ถูกต้องและจัดการ thread safety ให้ถูกต้องคือสิ่งที่ทำให้โค้ดแบบ parallel มีประสิทธิผลและถูกต้อง นี่จึงเป็นความรู้ระดับ senior ที่สำคัญและเกี่ยวข้องบ่อยสำหรับการพัฒนา C# ที่อ่อนไหวต่อประสิทธิภาพ