当前位置:首页 > C# > 正文

C#集合的线程安全包装详解(使用SyncRoot实现.NET集合同步)

在C#多线程编程中,当多个线程同时访问同一个集合时,很容易出现数据不一致、异常甚至程序崩溃的问题。为了解决这个问题,.NET 提供了多种线程安全机制,其中一种经典方式就是使用 SyncRoot 模式对集合进行线程安全包装。

C#集合的线程安全包装详解(使用SyncRoot实现.NET集合同步) C#线程安全集合 SyncRoot模式 .NET集合同步 C#多线程编程 第1张

什么是 SyncRoot?

SyncRoot 是 .NET 中 ICollection 接口定义的一个只读属性,类型为 object。它的作用是提供一个可用于同步(加锁)的对象,确保在多线程环境下对集合的操作是线程安全的。

需要注意的是:从 .NET Core 和 .NET 5+ 开始,SyncRoot 已被标记为过时(obsolete),官方推荐使用 ConcurrentQueue<T>ConcurrentBag<T> 等并发集合类。但在 .NET Framework 项目或学习目的中,理解 SyncRoot 仍然非常重要。

为什么需要线程安全包装?

考虑以下代码:

// 非线程安全的集合操作var list = new List<int>();Task.Run(() =>{    for (int i = 0; i < 1000; i++)        list.Add(i);});Task.Run(() =>{    for (int i = 0; i < 1000; i++)        list.Remove(0);});// 可能抛出 InvalidOperationException:集合已被修改

上述代码在多线程下运行极不稳定,因为 List<T> 本身不是线程安全的。这时候就需要线程安全包装。

使用 SyncRoot 实现线程安全包装

.NET 提供了 ArrayList.SynchronizedHashtable.Synchronized 等静态方法来返回一个线程安全的包装器。这些包装器内部使用 SyncRoot 进行加锁。

// 使用 SyncRoot 包装 ArrayListArrayList safeList = ArrayList.Synchronized(new ArrayList());// 现在可以在多线程中安全使用Task.Run(() =>{    for (int i = 0; i < 100; i++)    {        lock (safeList.SyncRoot)        {            safeList.Add(i);        }    }});Task.Run(() =>{    for (int i = 0; i < 50; i++)    {        lock (safeList.SyncRoot)        {            if (safeList.Count > 0)                safeList.RemoveAt(0);        }    }});

注意:即使使用了 Synchronized 包装器,在遍历集合时仍需手动加锁,否则仍可能出错:

// 正确的遍历方式lock (safeList.SyncRoot){    foreach (var item in safeList)    {        Console.WriteLine(item);    }}

自定义集合的 SyncRoot 实现

如果你正在开发自己的集合类,并希望支持 SyncRoot 模式,可以这样实现:

public class MyCollection : ICollection{    private readonly List<object> _innerList = new List<object>();    private readonly object _syncRoot = new object();    public object SyncRoot => _syncRoot;    public void Add(object item)    {        lock (_syncRoot)        {            _innerList.Add(item);        }    }    // 其他 ICollection 成员实现...}

现代替代方案:并发集合

虽然 SyncRoot 在 C#线程安全集合 的历史中扮演了重要角色,但如今更推荐使用 .NET 提供的并发集合类,例如:

  • ConcurrentQueue<T>:线程安全的先进先出队列
  • ConcurrentStack<T>:线程安全的后进先出栈
  • ConcurrentBag<T>:线程安全的无序集合
  • ConcurrentDictionary<TKey, TValue>:线程安全的字典

这些类内部已优化为无锁或细粒度锁,性能远优于全局加锁的 SyncRoot 方案。

总结

SyncRoot 是 .NET Framework 时代实现 C#多线程编程 中集合线程安全的重要机制。通过它,开发者可以对非线程安全的集合进行包装,确保在多线程环境下的数据一致性。尽管在现代 .NET 开发中已逐渐被并发集合取代,但理解 .NET集合同步 的原理对于掌握 C# 线程安全基础仍然至关重要。

无论你是维护老项目,还是学习底层原理,掌握 C#线程安全集合 的实现方式都将为你打下坚实的并发编程基础。