Thuộc tính Array.SyncRoot trả về một đối tượng dùng để khóa (lock) khi truy cập mảng từ nhiều luồng (thread), đảm bảo an toàn luồng (thread safety) cho các thao tác liệt kê (enumeration) hoặc sửa đổi mảng.
Tại sao cần SyncRoot?
- Mảng trong C# không an toàn luồng (not thread-safe) cho việc ghi/đọc đồng thời.
- Duyệt mảng (
foreach) trong khi luồng khác sửa đổi mảng → némInvalidOperationException. SyncRootcung cấp đối tượng khóa chuẩn cho mảng, dùng vớilock.
Ví dụ: Khóa mảng khi duyệt
using System;
using System.Threading;
class Program
{
static int[] sharedArray = { 23, 11, 32, 18, 87 };
static void Main()
{
// Luồng 1: Duyệt mảng
Thread t1 = new Thread(() =>
{
lock (sharedArray.SyncRoot)
{
foreach (object val in sharedArray)
{
Console.WriteLine($"Reader: {val}");
Thread.Sleep(50); // Giả lập xử lý
}
}
});
// Luồng 2: Sửa đổi mảng (cần lock cùng đối tượng)
Thread t2 = new Thread(() =>
{
lock (sharedArray.SyncRoot)
{
Thread.Sleep(100); // Đợi reader vào trước
sharedArray[0] = 999;
Console.WriteLine("Writer: Đã thay đổi phần tử đầu tiên");
}
});
t1.Start(); t2.Start();
t1.Join(); t2.Join();
}
}
Lưu ý quan trọng
lock(arr.SyncRoot)chỉ bảo vệ nếu tất cả luồng đều lock trên cùng đối tượng SyncRoot trước khi truy cập mảng.- Mảng 1 chiều (vector) có
SyncRoottrả về chính mảng đó (this). Mảng đa chiều trả về đối tượng wrapper. - Từ .NET 2.0+, khuyến nghị dùng Generic Collections (
List<T>,ConcurrentBag<T>...) thay vì mảng raw cho đa luồng. - Đối với
List<T>, dùnglock(((ICollection)list).SyncRoot)hoặc tốt hơn: dùngImmutableList<T>hoặcConcurrentBag<T>.
So sánh
| Cách làm | Ưu điểm | Nhược điểm |
|---|---|---|
lock(arr.SyncRoot) | Đơn giản, built-in | Coarse-grained lock, dễ deadlock nếu lồng nhau |
Concurrent Collections | Lock-free, hiệu năng cao | API phức tạp hơn |
Immutable Collections | Thread-safe by design | Tạo bản copy khi sửa đổi |