求教对Hashtable加锁和对Hashtable特定键值加锁的区别

bonat 2014-10-05 11:58:45
小弟在项目中用Hashtable实现了一个共享缓冲区,涉及多线程需要加锁(一写多读),考虑到一个线程一次只针对Hashtable中的一个key读写,所以我想是否可以只对要操作的key加锁,而不是对整个Hashtable加锁。
代码大致如下:
Hashtable ht;
......
if (ht.Contains(k)
{
lock(ht[k])
{
if (ht.Contains(k)
{
....
}
}
}

请问相比于直接lock(ht),在效率上会有多大的优势?
...全文
379 11 打赏 收藏 转发到动态 举报
写回复
用AI写文章
11 条回复
切换为时间正序
请发表友善的回复…
发表回复
  • 打赏
  • 举报
回复
一般来说,加锁的安全问题是从业务逻辑流程上说的,不是仅仅抠“底层”机制的。所以为了安全起见,通常我们都会在(哪怕)稍微“粗粒度”一点的对象上进行,在能够保证事先实例化完毕的对象上进行(而不是进行“是否存在”的判断)。例如,先实例化一个集合对象,然后在子线程中不用去判断这个集合对象是否存在(肯定一直存在)。
  • 打赏
  • 举报
回复
lz 的描述比较含糊,还是自己写集中业务逻辑操作的测试程序,然后并发几十个线程来反复运行几百次测试一下吧!测试往往能够帮你发现问题。 多个线程对同一个hashtable对象“加锁”,肯定可以非常方便地保证业务逻辑上的安全执行,因为实际上在相关的某几行代码上“所有线程只能排队通过”了嘛。如果你为了“效率”而改变加锁的对象,那么就有可能因为你的疏忽而造成一些问题。例如原本你希望两个线程因为某个单元对象(为锁标志)而互斥,可是一个线程从hashtable上找到了单元,而另一个线程却没有找到单元,这时候是否会造成你原来设计的业务逻辑路线执行起来根本错误?这不得而知,因为你描述的过于简单了,看不出来。 另外,你描述的代码也是有错误的。
if (ht.Contains(k)
{
    lock(ht[k])
这里应该就有问题,第一条语句的判断条件就算是返回true,也不能保证第二个语句的 ht[k] 不会是 null。所以它需要先对ht加锁,然后你才能保证对 ht[k] 可以加锁。这种错误需要并发测试才能发现,随便用手工调试方法“敲两下键盘、动动鼠标”来测试则不容易发现。
於黾 2014-10-08
  • 打赏
  • 举报
回复
如果你要操作的是个静态数组,数组长度是固定的,每一项都是值类型,那么就根本没有任何必要加锁 而你要操作的是个Hashtable,如果长度永远不变,你也根本没有必要为操作每一项而加锁(本来复杂度就是O(1),原子操作) 但是为什么一定要为Hashtable的操作加锁呢? 因为可以add和remove 这两个操作,会对整个结构的长度造成影响,不加锁,很容易索引溢出.
  • 打赏
  • 举报
回复
引用 3 楼 bonat 的回复:
谢谢指点! 我可能没说清楚,我真实的意思不是锁住整个Hashtable或者锁住Hashtable的某个key。我想表达的是: 如果每个线程读写Hashtable的代码都用lock(ht)同步,那么所有线程都是互斥的;但是如果根据线程所要操作Hashtable的key,使用lock(ht[key])同步,那么将只会使得操作同一个key的线程互斥,换句话说,如果两个线程操作不同的key,那么可以实现这两个线程的并发。 我上面的理解对不对啊?请前辈们给指点一下,能否给个示例代码,感激不尽!
如果之前了解 lock 机制,这个应用还要“代码示例”?直接用到你的程序中就是了。
jy251 2014-10-07
  • 打赏
  • 举报
回复
我也看成了你想锁住某个hashtabel或者他的key了 如果你想要完全锁住某个资源师基本不可能的,现在的这些锁都只是锁住某个代码段而已。如果你想实现对某个资源的锁,至少是看起来像是锁住了某个资源,你可以这样做 建立一个函数,比如某个hashtable的read函数,比如啊 class MyHashTable { static object lockAsyncObj = new object(); hasttable ht; public object readhash() { lock(lockAsyncObj) { //读取操作,并返回 } } public void WriteHast() { lock(lockAsyncObj) { //你的写操作 } } } 你所有的需要调用这个资源的时候,都使用这一个readhash和write函数,资源就达到了你想要的锁的概念。或者你也可以使用扩展方法直接给hasttable添加一个方法来获取或者修改他的值,而这个方法跟我上面写的那个read于write方法一个意思,这样至少看起来更像是锁住了某个资源。 代码是伪代码,看懂意思就行了
zhangyuntian 2014-10-07
  • 打赏
  • 举报
回复
你可能是想表达,线程并发时,某行的读写不影响其它行的读写。 支持sp1234 的说法!
bonat 2014-10-07
  • 打赏
  • 举报
回复
自己顶一下,求各位大哥给指定一下,项目急用啊。。。
  • 打赏
  • 举报
回复
你纠结的那一堆东西都可以忘掉,先理顺了“加锁”概念,然后按照正常的逻辑再去表达你到底要做什么。
  • 打赏
  • 举报
回复
引用 楼主 bonat 的回复:
小弟在项目中用Hashtable实现了一个共享缓冲区,涉及多线程需要加锁(一写多读),考虑到一个线程一次只针对Hashtable中的一个key读写,所以我想是否可以只对要操作的key加锁,而不是对整个Hashtable加锁。
这个概念是错误的。我们可以这样说,因为代码看上去就是这样“念”的,但是我们知道其内部的机制。而你望文生义,对内部机制理解错误了。 lock(obj) 这个语法,不是你认为的那种“加锁”概念,是指以obj为锁(为索引)对当前的可执行语句进行阻塞。那么比如说一个代码是 lock(abc),而另一个代码是 lock(abc的某个属性),这就根本不会互为阻塞。就是这样的意思。 根本没有你认为的那种实体锁。
bonat 2014-10-05
  • 打赏
  • 举报
回复
引用 1 楼 sp1234 的回复:
[quote=引用 楼主 bonat 的回复:] 小弟在项目中用Hashtable实现了一个共享缓冲区,涉及多线程需要加锁(一写多读),考虑到一个线程一次只针对Hashtable中的一个key读写,所以我想是否可以只对要操作的key加锁,而不是对整个Hashtable加锁。
这个概念是错误的。我们可以这样说,因为代码看上去就是这样“念”的,但是我们知道其内部的机制。而你望文生义,对内部机制理解错误了。 lock(obj) 这个语法,不是你认为的那种“加锁”概念,是指以obj为锁(为索引)对当前的可执行语句进行阻塞。那么比如说一个代码是 lock(abc),而另一个代码是 lock(abc的某个属性),这就根本不会互为阻塞。就是这样的意思。 根本没有你认为的那种实体锁。[/quote] 谢谢指点! 我可能没说清楚,我真实的意思不是锁住整个Hashtable或者锁住Hashtable的某个key。我想表达的是: 如果每个线程读写Hashtable的代码都用lock(ht)同步,那么所有线程都是互斥的;但是如果根据线程所要操作Hashtable的key,使用lock(ht[key])同步,那么将只会使得操作同一个key的线程互斥,换句话说,如果两个线程操作不同的key,那么可以实现这两个线程的并发。 我上面的理解对不对啊?请前辈们给指点一下,能否给个示例代码,感激不尽!

110,538

社区成员

发帖
与我相关
我的任务
社区描述
.NET技术 C#
社区管理员
  • C#
  • Web++
  • by_封爱
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告

让您成为最强悍的C#开发者

试试用AI创作助手写篇文章吧