一个 .Net 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 会自动对单个写的操作加锁。

posted @ 2008-07-09 09:14  叶子飞起来了  阅读(225)  评论(0编辑  收藏  举报