线程安全Dictionary.Add [英] Thread-safe Dictionary.Add

查看:278
本文介绍了线程安全Dictionary.Add的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

Dictionary.Add()线程安全的,当你只插入?

Is Dictionary.Add() thread safe when you only insert?

我有一个code表示插入来自多个线程的键,我还需要各地Dictionary.Add()锁定

I've got a code that insert keys from multiple-threads, do I still need locking around Dictionary.Add()

我同时增加了新的密钥得到这个异​​常:

I got this exception while adding a new key:

Exception Source:    mscorlib
Exception Type: System.IndexOutOfRangeException
Exception Message:   Index was outside the bounds of the array.
Exception Target Site: Insert

尽管这是相当难得的。我知道,词典不是线程安全的,虽然我以为只有调用。新增不会引起任何问题。

Although it's quite rare. I know that Dictionary is not thread-safe although I thought that only calling .Add wouldn't cause any problems.

推荐答案

字典是不是线程安全的所有,无论你只给它添加或不 - 有一些内部结构给它保持同步(尤其是当内部hashbuckets得到调整大小),该需要。

Dictionary is not thread-safe at all, regardless of whether you only add to it or not - there are a few internal structures to it that need to be kept in sync (especially when the internal hashbuckets get resized).

您要么必须实现它自己身边的任何操作锁定,或者如果你在.NET 4.0中,你可以使用新的ConcurrentDictionary - 这是绝对精彩 - 而这完全是线程安全的。

You either have to implement your own locking around any operation on it, or if you're in .Net 4.0 you can use the new ConcurrentDictionary - which is absolutely fantastic - and which is totally thread-safe.

这说的 - 还有另外一个技术,你可以使用 - 但它会需要一些调整,这取决于那种你插入到你的字典数据,以及所有的按键是否保证唯一的:

That said - there is another technique you can use - but it'll require a bit of tweaking depending upon the kind of data you're inserting into your dictionary, and whether all your keys are guaranteed unique:

给每个线程它可以将插入自己的私人字典。

Give each thread it's own private dictionary that it inserts into.

当每个线程完成,整理所有的词典在一起,并将它们合并成一个更大的;如何处理重复键是由你。例如,如果你通过一键缓存项的列表,那么你可以简单地合并的每个同键列表为一体,并把它放在主字典。

When each thread finishes, collate all the dictionaries together and merge them into a bigger one; how you handle duplicate keys is up to you. For example, if you're caching lists of items by a key, then you can simply merge each same-keyed list into one and put it in the master dictionary.

所以,作为您的意见说,你需要最好的方法的想法(锁定或合并)的性能等等。我不能告诉你这是什么会;它最终将需要进行基准测试。我去看看我可以提供一些指导,但是:)

So as your comments say, you need an idea of the best method (lock or merge) for performance etc. I can't tell you what this will be; ultimately it will need to be benchmarked. I'll see if I can offer some guidance, though :)

首先 - 如果你有什么想法,有多少项目你Dictionar(Y / IES)将最终需要,使用(INT)的构造,以尽量减少调整

Firstly - if you have any idea how many items your Dictionar(y/ies) will ultimately need, use the (int) constructor to minimize resizing.

合并操作可能是最好的;因为没有线程将相互干扰。除非涉及到当两个对象共享同一个密钥特别漫长的过程;在这种情况下,迫使这一切发生在单个线程在操作结束时可能最终通过并行的第一阶段归零所有性能增益!

The merge operation is likely to be best; since none of the threads will be interfering with each other. Unless the process involved when two objects share the same key is particularly lengthy; in which case forcing it all to happen on a single thread at the end of the operation might end up zeroing all the performance gains by parallelizing the first stage!

同样,有潜在的内存的头等大事,因为你将有效地克隆字典,因此,如果最后的结果是足够大,你可能最终会消耗大量的资源;理所当然的,但 - 他们将被释放

Equally, there's potentially memory concerns there since you will effectively be cloning the dictionary, so if the final result is large enough you could end up consuming lots of resources; granted, though - they will be released.

如果是需要进行线程级时的关键已经是一个决定的情况下,present,那么您将需要一个锁(){}结构。

If it's the case that a decision needs to be made the thread-level when a key is already present, then you will need a lock(){} construct.

在一本字典,这通常采用以下形状:

Over a dictionary, this typically takes the following shape:

readonly object locker = new object();
Dictionary<string, IFoo> dictionary = new Dictionary<string, IFoo>();

void threadfunc()
{
  while(work_to_do)
  {
    //get the object outside the lock
    //be optimistic - expect to add; and handle the clash as a 
    //special case
    IFoo nextObj = GetNextObject(); //let's say that an IFoo has a .Name
    IFoo existing = null;
    lock(locker)
    {
      //TryGetValue is a god-send for this kind of stuff
      if(!dictionary.TryGetValue(nextObj.Name, out existing))
        dictionary[nextObject.Name] = nextObj;
      else
        MergeOperation(existing, nextObject);
    }
  }
}

现在如果 MergeOperation 确实的慢;那么你可以考虑解除锁定,创建重新presents现有及新对象的合并一个克隆的对象,然后重新获取锁。然而。 - 需要检查,现有的对象的状态还没有第一锁定和第二之间改变的一个可靠的方式(一版本号是这个有用)

Now if that MergeOperation is really slow; then you might consider releasing the lock, creating a cloned object that represents the merge of the existing and the new object, then re-acquiring the lock. However - you need a reliable way of checking that the state of the existing object hasn't changed between the first lock and the second (a version number is useful for this).

这篇关于线程安全Dictionary.Add的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆