Parallel.ForEach (namespace System.Threading.Tasks) chạy vòng lặp trên nhiều thread tự động. Mặc định dùng ThreadPool, số thread tùy máy. Để giới hạn, dùng ParallelOptions.MaxDegreeOfParallelism.
Ví dụ cơ bản
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Threading.Tasks;
class Program
{
static void Main()
{
var items = new List<int>(Enumerable.Range(1, 20));
Console.WriteLine("=== Parallel.ForEach mặc định ===");
var sw = Stopwatch.StartNew();
Parallel.ForEach(items, item => ProcessItem(item));
Console.WriteLine($"Time: {sw.ElapsedMilliseconds}ms");
Console.WriteLine("\n=== Parallel.ForEach MaxDegreeOfParallelism = 2 ===");
sw.Restart();
Parallel.ForEach(items, new ParallelOptions { MaxDegreeOfParallelism = 2 }, item => ProcessItem(item));
Console.WriteLine($"Time: {sw.ElapsedMilliseconds}ms");
Console.ReadLine();
}
static void ProcessItem(int item)
{
Console.WriteLine($"Item {item,2} - Thread {Thread.CurrentThread.ManagedThreadId}");
Thread.Sleep(100); // Giả lập work
}
}
Kết quả mẫu (MaxDegreeOfParallelism = 2)
Item 1 - Thread 1
Item 2 - Thread 4
Item 3 - Thread 1
Item 4 - Thread 4
Item 5 - Thread 1
... (chỉ 2 thread cùng chạy)
Khi nào nên giới hạn?
- I/O bound (DB, HTTP, File): Giới hạn tránh exhaustion (connection pool, socket, file handle). VD: DB connection pool = 100 -> set MaxDegreeOfParallelism = 50.
- CPU bound: Thường không cần giới hạn (mặc định = số logical cores). Giới hạn nếu muốn dành core cho task khác.
- Rate limiting API: API cho phép 10 req/s -> set 5-10.
- Shared resource: Ghi file cùng lúc, lock contention.
Parallel.ForEachAsync (.NET 6+)
// Async version - tốt cho I/O bound
await Parallel.ForEachAsync(urls, new ParallelOptions { MaxDegreeOfParallelism = 5 }, async (url, ct) =>
{
using var client = new HttpClient();
var response = await client.GetAsync(url, ct);
// process response
});
Lưu ý quan trọng
MaxDegreeOfParallelism = 1-> chạy tuần tự (giống foreach).MaxDegreeOfParallelism = -1(mặc định) -> không giới hạn (do ThreadPool quản lý).- Giá trị > số logical cores có thể giảm hiệu suất do context switching (CPU bound).
- Parallel.ForEach block thread hiện tại cho đến khi xong. Dùng
Task.Runnếu gọi từ UI thread. - Exception: AggregateException gói tất cả exception từ các thread.
Partitioner (Tùy chỉnh chia việc)
var partitioner = Partitioner.Create(0, 1000, 100); // range partitioning
Parallel.ForEach(partitioner, (range, loopState) =>
{
for (int i = range.Item1; i < range.Item2; i++)
ProcessItem(i);
});