SyncRoot 属性

本文探讨了在.NET Framework中锁定Hashtable的正确方法。通过实验对比锁实例与锁SyncRoot属性的不同,并介绍了使用Hashtable.Synchronized创建自动线程同步的Hashtable。

今天同事告诉我, 锁 hashtable 应该锁它的 SyncRoot 属性而不应该锁它的实例, 例如:

Hashtable ht = new Hashtable();
lock(ht.SyncRoot)
{
...
}

看了 .Net Framework 文档, 给的例子也是锁 SyncRoot 属性, 说如果锁实例的话不能保证在并发情况下的同步, 我很疑惑, 为什么不能锁 hashtable 实例本身呢?

做了个实验, 两个线程 A 和 B, 用锁实例和锁 SyncRoot 两种方式测试, 都没有问题, 结果是一样的。

后来, 用 Hashtable.Synchronized 创建自动线程同步的 hashtable, 终于明白了 SyncRoot 的作用。先说说自动线程同步的 Hashtable: 如果 Hashtable 要允许并发读但只能一个线程写, 要这么创建 Hashtable 实例:

Hashtable hashtable = Hashtable.Synchronized(new Hashtable());

这样, 如果有多个线程并发的企图写 hashtable 里面的 item, 则同一时刻只能有一个线程写, 其余阻塞; 对读的线程则不受影响。

测试的代码是这样的:

Hashtable _hashtable = Hashtable.Synchronized(new Hashtable());

public void TestLock()
{
Thread t1 = new Thread(new ThreadStart(SyncFunctionA));
Thread t2 = new Thread(new ThreadStart(SyncFunctionB));

t1.Start();
t2.Start();

Thread.Sleep(8000);

Console.WriteLine("hashtable[" + _key_a + "] = " + _hashtable[_key_a]);
}

private void SyncFunctionA()
{
lock (_hashtable.SyncRoot)
{
Thread.Sleep(5000);
_hashtable[_key_a] = "Value set by SyncFunctionA";
}
}

private void SyncFunctionB()
{
Console.WriteLine("hashtable[" + _key_a + "] = " + _hashtable[_key_a]);
_hashtable[_key_a] = "Value set by SyncFunctionB";

}

为了清楚的看到效果, 线程 A 用了锁, 并睡眠 5 秒, 睡醒后设置一下 hashtable 里的 item. 线程 B 先读一下 hashtable 里的 item, 再写 hashtable 里的 item。因为对 SyncRoot 加了锁, 即使线程 B 没有显式的对 hashtable 加锁, 但在 _hashtable[_key_a] = "Value set by SyncFunctionB" 一句上也会被 hashtable 自动锁住, 直到线程 A 释放掉 SyncRoot 锁为止。如果线程 A 不是锁 SyncRoot 而是锁 hashtable 实例本身, 那么线程 B 不会在 _hashtable[_key_a] = "Value set by SyncFunctionB" 上被自动锁住。

所以, 总结如下:

如果想锁整个 hashtable, 包括读和写, 即不允许并发的读和写, 那应该锁 hashtable 实例;
如果想允许并发的读, 不允许并发的写, 那应该创建 Synchronized 的 hashtable, 并对要加锁的一块代码用 SyncRoot 锁住, 如果不需要对一块代码加锁, 则 hashtable 会自动对单个写的操作加锁。

以下是一个知识点全面且经典的 C# `ArrayList` 例程,涵盖了动态数组的基本操作、属性访问和常见功能。该程序演示了如何创建、添加元素、删除元素、查找元素、排序以及使用线程同步等特性。 ```csharp using System; using System.Collections; class Program { static void Main(string[] args) { // 创建一个 ArrayList 实例 ArrayList arrayList = new ArrayList(); // 添加元素(支持多种数据类型) arrayList.Add("Apple"); arrayList.Add(123); arrayList.Add(true); arrayList.Add(3.14); Console.WriteLine("初始 ArrayList 内容:"); PrintArrayList(arrayList); // 插入元素到指定位置 arrayList.Insert(1, "Inserted Item"); Console.WriteLine("\n插入元素后的内容:"); PrintArrayList(arrayList); // 删除特定值的元素 arrayList.Remove("Inserted Item"); Console.WriteLine("\n删除 'Inserted Item' 后的内容:"); PrintArrayList(arrayList); // 删除指定索引处的元素 arrayList.RemoveAt(0); Console.WriteLine("\n删除索引 0 处的元素后的内容:"); PrintArrayList(arrayList); // 查找元素是否存在 bool containsNumber = arrayList.Contains(123); Console.WriteLine($"\n是否包含数字 123?{containsNumber}"); // 获取索引 int index = arrayList.IndexOf(123); Console.WriteLine($"数字 123 的索引是: {index}"); // 排序(注意:混合类型时可能抛出异常) try { arrayList.Sort(); Console.WriteLine("\n排序后的内容:"); PrintArrayList(arrayList); } catch (Exception ex) { Console.WriteLine($"排序失败: {ex.Message}"); } // 属性访问 Console.WriteLine($"\nCount(实际元素数): {arrayList.Count}"); Console.WriteLine($"Capacity(当前容量): {arrayList.Capacity}"); Console.WriteLine($"IsFixedSize(是否固定大小): {arrayList.IsFixedSize}"); Console.WriteLine($"IsReadOnly(是否只读): {arrayList.IsReadOnly}"); Console.WriteLine($"IsSynchronized(是否线程安全): {arrayList.IsSynchronized}"); Console.WriteLine($"SyncRoot(用于线程同步的对象): {arrayList.SyncRoot}"); // 使用线程安全方法访问(推荐方式) lock (arrayList.SyncRoot) { Console.WriteLine("\n在锁内执行操作以确保线程安全..."); arrayList.Add("Thread-safe Item"); } Console.WriteLine("\n线程安全操作后的内容:"); PrintArrayList(arrayList); } // 辅助方法:打印 ArrayList 中的所有元素 static void PrintArrayList(ArrayList list) { foreach (var item in list) { Console.WriteLine(item); } } } ``` ### 程序说明: 1. **基本操作**: - 使用 `Add()` 和 `Insert()` 方法向 `ArrayList` 中添加或插入元素。 - 使用 `Remove()` 和 `RemoveAt()` 删除元素。 - 使用 `Contains()` 和 `IndexOf()` 进行查找。 2. **排序**: - 调用 `Sort()` 对集合进行排序。由于 `ArrayList` 支持多种数据类型,排序时可能会因类型不兼容而抛出异常。 3. **属性访问**: - 通过 `Count` 和 `Capacity` 属性了解集合的实际元素数量和内部数组容量。 - 使用 `IsFixedSize`、`IsReadOnly` 和 `IsSynchronized` 判断集合的状态。 4. **线程安全**: - 使用 `SyncRoot` 和 `lock` 语句来实现线程安全的访问。 ### 输出结果示例: ``` 初始 ArrayList 内容: Apple 123 True 3.14 插入元素后的内容: Apple Inserted Item 123 True 3.14 删除 'Inserted Item' 后的内容: Apple 123 True 3.14 删除索引 0 处的元素后的内容: 123 True 3.14 是否包含数字 123?True 数字 123 的索引是: 0 排序失败: At least one element in the array is of a type that is not assignable to the type of the destination array. Count(实际元素数): 3 Capacity(当前容量): 4 IsFixedSize(是否固定大小): False IsReadOnly(是否只读): False IsSynchronized(是否线程安全): False SyncRoot(用于线程同步的对象): System.Collections.ArrayList 在锁内执行操作以确保线程安全... 线程安全操作后的内容: 123 True 3.14 Thread-safe Item ```
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值