Ngoài async/ cho I/O, .NET cung cấp cho — chạy các tính toán trên nhiều lõi. Sự phân biệt then chốt: / là cho đồng thời I/O (chờ không chặn), còn , , và PLINQ là để song song hóa công việc nặng về CPU.
Ngoài async/ cho I/O, .NET cung cấp cho — chạy các tính toán trên nhiều lõi. Sự phân biệt then chốt: / là cho đồng thời I/O (chờ không chặn), còn , , và PLINQ là để song song hóa công việc nặng về CPU.
awaitasyncawaitTask.RunParallelasync/await → đồng thời gắn với I/O. Giải phóng thread trong lúc chờ (DB, mạng).
KHÔNG thêm song song hóa CPU.
Song song → công việc gắn với CPU trải trên NHIỀU LÕI (tính toán, xử lý).
Dùng Task.Run, Parallel.For/ForEach, PLINQ.
// chạy một tính toán nặng CPU trên một thread của thread pool (đừng chặn caller)
int result = await Task.Run(() => ExpensiveComputation());
// chạy nhiều cái song song và kết hợp
var tasks = items.Select(item => Task.Run(() => Process(item)));
var results = await Task.WhenAll(tasks); // chờ tất cả
Task.Run lập lịch công việc trên một thread của thread pool — dùng nó để song song hóa tính toán gắn với CPU (KHÔNG phải cho I/O, nơi async/await đã đủ mà không cần thread thêm).
// Parallel.For/ForEach — xử lý một collection trải trên các lõi
Parallel.ForEach(items, item => Process(item)); // tự động dùng nhiều lõi
Parallel.For(0, 1000, i => Compute(i));
// PLINQ — LINQ song song
var results = data.AsParallel().Where(x => IsValid(x)).Select(Transform).ToList();
Parallel.For/ForEach và PLINQ (.AsParallel()) tự động phân phối công việc trên các lõi — tuyệt vời cho xử lý nặng CPU các phần tử độc lập. (Chỉ đáng làm khi công việc thực sự gắn với CPU và có quy mô — có chi phí phụ.)
// code song song truy cập trạng thái CHIA SẺ cần đồng bộ hóa hoặc kiểu an toàn thread
lock (_lock) { _counter++; } // lock để loại trừ lẫn nhau
Interlocked.Increment(ref _counter); // atomic không cần lock
var dict = new ConcurrentDictionary<string, int>(); // collection an toàn thread
Hiểu Task và song song hóa ngoài async/await là kiến thức cấp senior quan trọng để xây dựng các ứng dụng C# hiệu năng, và sự phân biệt then chốt giữa đồng thời I/O và song song hóa CPU là cái nhìn cốt lõi — một điểm gây nhầm lẫn phổ biến. async/await xử lý đồng thời gắn với I/O (giải phóng thread trong lúc chờ) nhưng không thêm song song hóa CPU, trong khi công việc gắn với CPU (tính toán, xử lý dữ liệu) hưởng lợi từ song song hóa thực sự trên các lõi qua Task.Run (chuyển công việc CPU sang các thread của thread pool), Parallel.For/ForEach, và PLINQ (.AsParallel()).
Biết công cụ nào phù hợp — async/await cho I/O, các công cụ song song TPL cho công việc gắn với CPU — là thiết yếu để tối ưu hiệu năng hiệu quả, vì dùng sai (ví dụ kỳ vọng async tăng tốc tính toán, hoặc khởi tạo thread cho I/O mà async xử lý hiệu quả) là vô ích.
Quan trọng không kém là hiểu an toàn thread: code song song truy cập trạng thái chia sẻ cần đồng bộ hóa (lock, Interlocked cho các thao tác atomic, hoặc các collection an toàn thread như ConcurrentDictionary) để tránh race condition — một mối quan tâm về tính đúng đắn then chốt trong code song song.
Hiểu sự phân biệt I/O-vs-CPU, các công cụ song song hóa (Task.Run, Parallel, PLINQ), và các yêu cầu an toàn thread là có giá trị để viết C# đồng thời và song song đúng đắn, hiệu năng.
Vì các ứng dụng hiện đại thường cần cả đồng thời I/O lẫn song song hóa CPU, và vì chọn đúng cách tiếp cận và xử lý an toàn thread đúng là điều khiến code song song hiệu quả và đúng đắn, đây là kiến thức senior quan trọng, thường xuyên liên quan để phát triển C# nhạy cảm hiệu năng.