关于HashTable线程安全问题

bonat 2014-08-16 10:01:27
文档中说HashTable是线程安全的。
如果有多个线程读一个HashTable,大概执行value = ht[key]这种操作,线程安全是不是意味着多个线程均能读到正确的值,但是这中间是不是会存在线程同步问题,导致多个线程读的效率降低呢?
请明白人给指定一下吧,多谢!
...全文
261 5 打赏 收藏 转发到动态 举报
写回复
用AI写文章
5 条回复
切换为时间正序
请发表友善的回复…
发表回复
Forty2 2014-08-16
  • 打赏
  • 举报
回复
引用 MSDN:
Hashtable 是线程安全的,可由多个读取器线程和一个写入线程使用
要理解这个写入安全,可以先来看一个不安全的例子:

class MyList
{
    int count;
    int[] items = new int[1024];

    public void Add(int val)
    {
        // 方法一
        count++;
        items[count - 1] = val;
    }
    public int GetLast()
    {
        return items[count - 1];
    }
}
该实现在添加(写入)的时候,一先增加总数,二再写入数组。这种情况,它就对读操作就不是线程安全的。 因为另外一个线程GetLast进行读操作的时候,可能正好发生在一和二操作之间,它将返回的,将是一个未定义的元素。 一个简单的更改就可以避免这个问题(限于一个写入线程): // 方法二 items[count] = val; count++; 先把内容写入,再提交总数变化。如此以来,提交后,读线程一定能得到正确的数据;提交前,读线程也看不到变化,同样能得到正确的数据。 这个例子要解释的就是,要作到单一写入线程安全,并不一定要进行同步操作。因此,不需要有‘导致多个线程读的效率降低呢’的担忧。如果你有心确认这点,可以用Reflector等查看Hashtable的代码,看看有何种的同步机制。 有趣的是,Hashtable.Synchronized(hashtable1),可以返回一个多重写线程安全Hashtable。实际上,它返回了一个继承于Hashtable的SyncHashtable,在添加的时候,对SyncRoot加锁:

      public override void Add(object key, object value)
        {
            lock (this._table.SyncRoot)
            {
                this._table.Add(key, value);
            }
        }
要小心的是,Hashtable.Synchronized(hashtable1)保证每个独立操作是安全的,但不保证多个操作的线程安全,比如:

Hashtable syncedTable = Hashtable.Synchronized(new Hashtable());
void Thread1()
{
    syncedTable.Add("A", 123);
    int i = (int)syncedTable["A"];  //可能会出现异常,如果“A"被Thread2删除了
}
void Thread2()
{
    syncedTable.Remove("A");
}
  • 打赏
  • 举报
回复
什么加锁、同步之类的操作,跟“线程安全”根本无关。 如果你再多线程操作时测试到这种冲突,那么该进行这方面的处理就要进行这方面的处理。不要扯上“线程安全”这个词儿就行了。
  • 打赏
  • 举报
回复
引用 楼主 bonat 的回复:
文档中说HashTable是线程安全的。 如果有多个线程读一个HashTable,大概执行value = ht[key]这种操作,线程安全是不是意味着多个线程均能读到正确的值,但是这中间是不是会存在线程同步问题,导致多个线程读的效率降低呢? 请明白人给指定一下吧,多谢!
你根本没有理解什么叫做线程安全性啊。 如果一个线程先查找然后插入数据,另一个线程先查找然后删除数据,第三个线程先查找然后改变数据,那么这“安全吗”?肯定不安全。“线程安全”的 HashTable 肯定不能支持这种业务逻辑的线程安全性。如果你把“多个线程均能读取到正确的值”说成是“线程安全性”,就完全是错误的。 线程不安全的对象,如果你有两个线程都在进行插入数据的操作时,可能一个代码刚走到一半,由于另外一个代码的干扰,而造成这个插入操作出现了“诡异的”错误,这种错误层出不穷根本不可能预知在哪一条语句可能出错。这就是线程不安全的。于是程序就进行了必要的“管理线程保护”,使得针对同一个HashTable对象的两个并发插入操作不会产生冲突。这就是线程安全的。 绝不是说业务逻辑是线程安全的!没有任何线程安全的对象能够保证在不同线程中其业务逻辑执行过程是存取安全的。你完全可能刚刚查询还有数据,然后瞬间再去取数据时已经不存在了,或者是值跟一瞬间之前判断的值其实是不一样了。线程安全的对象并不保证不会出现这种 bug。这需要你的业务逻辑处理程序去处理。
Forty2 2014-08-16
  • 打赏
  • 举报
回复
用lock可以。不过多个线程下锁的token必须是同一个(每次临时创建一个token不会有作用)。
bonat 2014-08-16
  • 打赏
  • 举报
回复
谢谢详细的回复! 您的意思应该是多线程读HashTable不会存在性能问题,我理解的没错吧 还有个问题,如果需要多线程写入HashTable的话(调用同一写入方法),除了使用Hashtable.Synchronized外,是否还可以这样:

void output(){
object token= new object();
lock (token)
{
    写入操作
}}

110,566

社区成员

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

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

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