5

我有一个带有固定键集合的字典,我在程序开始时创建了它。后来,我有一些线程用值更新字典。

  • 一旦线程开始,就不会添加或删除任何对。
  • 每个线程都有自己的密钥。意思是,只有一个线程可以访问某个 key
  • 线程可能会更新值

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

更新:

谢谢大家的回答,

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

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

那么,我应该锁定字典吗?并且 - 当您现在拥有完整版时,字典是正确的选择吗?

谢谢!

4

6 回答 6

9

来自 MSDN

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

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

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

于 2013-10-15T10:01:36.657 回答
2

使用 a 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被评估时,所有 3000ValueChangers将同时运行,产生一个IEnumerable<KeyValuePair<int, object>>.

线程之间不会有交互,因此不会出现并发问题。

如果你想把结果变成Dictionary你可以的,

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

这在您的情况下可能有用,也可能没有用,但是如果没有更多细节,很难说。

于 2013-10-15T10:01:10.710 回答
2

让我们一次解释一种解释来处理您的问题。

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

不,你没有。

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

是的,你绝对应该。

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

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

第三种解释:鉴于我给出的上下文,字典是正确的选择吗?

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

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

于 2013-10-15T10:03:30.700 回答
1

如果每个线程仅访问一个“值”并且您不关心其他线程,我会说您根本不需要字典。您可以使用ThreadLocalThreadStatic变量。

如果你需要一个Dictionary,你肯定需要一把锁。

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

于 2013-10-15T10:01:11.403 回答
0

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

还要检查一下:使字典访问线程安全?

于 2013-10-15T10:02:35.083 回答
0

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

所以:

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

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

不要尝试对键做同样的事情。必然地,它们应该是不可变的

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

于 2013-10-15T10:21:20.190 回答