字典上的多线程与固定键

时间:2013-10-15 09:56:23

标签: c# multithreading dictionary

我有一个包含固定密钥集的字典,我在程序开头创建。后来,我有一些线程用值更新字典。

  • 线程启动后,不会添加或删除任何对。
  • 每个帖子都有自己的密钥。意思是,只有一个线程可以访问某个密钥
  • 该主题可能更新值

问题是,我应该锁定字典吗?

更新

谢谢大家的回答,

当我问这个问题时,我试图简化这种情况,只是为了理解字典的行为。

为了清楚起见,这里是完整版: 我有一个包含~3000个条目(固定密钥)的字典,并且我有多个线程访问密钥(共享资源),但我知道只有一个线程一次访问一个密钥条目。

所以,我应该锁定字典吗?并且 - 当你现在拥有完整版本时,是一本正确选择的词典吗?

谢谢!

6 个答案:

答案 0 :(得分:4)

来自MSDN

只要不修改集合,词典就可以同时支持多个阅读器。

要允许多个线程访问集合以进行读写,您必须实现自己的同步。

有关线程安全的替代方法,请参阅ConcurrentDictionary<TKey, TValue>.

答案 1 :(得分:2)

使用ConcurrentDictionary,不要重新发明轮子。

更好的是,重构您的代码以避免这种不必要的争用。


如果线程之间没有通信,你可以这样做:

假设一个改变值的函数。

private static KeyValuePair<TKey, TValue> ValueChanger<TKey, TValue>(
        KeyValuePair<TKey, TValue> initial)
{
    // I don't know what you do so, i'll just return the value.
    return initial;
}

假设你有一些起始数据,

var start = Enumerable.Range(1, 3000)
                .Select(i => new KeyValuePair<int, object>(i, new object()));

你可以像这样一次处理它们,

var results = start.AsParallel().Select(ValueChanger);

当评估results时,所有3000 ValueChangers将同时运行,产生IEnumerable<KeyValuePair<int, object>>

线程之间不会有交互,因此没有可能的并发问题。

如果您希望将结果转换为Dictionary

var resultsDictionary = results.ToDictionary(p => p.Key, p => p.Value);

这在你的情况下可能有用,也可能没有用,但没有更详细的说法很难说。

答案 2 :(得分:2)

让我们一次解释你的问题。

第一种解释:鉴于Dictionary<TKey, TValue>是如何实现,根据我给出的上下文,我是否需要锁定字典?

不,你没有。

第二种解释:鉴于Dictionary<TKey, TValue是如何记录,根据我给出的上下文,我是否需要锁定字典?

是的,你绝对应该。

由于类型被记录为不是线程安全的,因此无法保证今天可能正常的访问在明确的多线程世界中都可以。这允许程序员对类型的状态和完整性做出某些假设,否则它们必须在保证中构建。

对.NET或整个新版本的修补程序或更新可能会更改实现并使其中断,这是您的错误,因为它依赖于未记录的行为。

第三种解释:根据我给出的背景,字典是正确的选择吗?

不,不是。要么切换到线程安全类型,要么根本不使用字典。为什么不在每个线程中使用一个变量呢?

结论:如果您打算使用字典,请锁定字典。如果可以切换到别的东西,那就去做吧。

答案 3 :(得分:1)

如果每个线程只访问一个“值”,如果你不关心别人,我会说你根本不需要一个字典。您可以使用ThreadLocalThreadStatic变量。

如果你需要Dictionary,你肯定需要锁定。

如果您使用.Net 4.0或更高版本,我强烈建议您使用ConcurrentDictionary,使用ConcurrentDictionary时无需同步访问权限,因为它已经是“ThreadSafe”。

答案 4 :(得分:0)

Diectionary不是线程安全的,但在你的代码中你不必这样做;你说一个线程更新一个值,所以你没有多线程问题! 我没有代码,所以我不确定100%。

同时检查:Making dictionary access thread-safe?

答案 5 :(得分:0)

如果您没有添加键,只是修改值,为什么不通过将复杂对象存储为值并修改复杂类型中的值来完全消除直接写入Dictionary的需要。这样,您就会尊重字典的线程安全约束。

所以:

class ValueWrapper<T>
{
    public T Value{get;set;}
}
//...
var myDic = new Dictionary<KeyType, ValueWrapper<ValueType>>();
//...
myDic[someKey].Value = newValue;

您现在不是直接写字典,但可以修改值。

请勿尝试对密钥执行相同操作。必要时,它们应该是不可改变的

鉴于约束“我知道一次只有一个线程正在访问一个密钥条目”,我认为你没有任何问题。