Computer >> Hướng Dẫn Máy Tính >  >> Lập Trình >> C#

Giới hạn độ song song (MaxDegreeOfParallelism) cho Parallel.ForEach trong C#

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.Run nế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);
});