字典更新线程安全

时间:2013-10-30 15:10:42

标签: c# dictionary thread-safety

以下代码线程是否安全?

var dict = new Dictionary<int, string>()
    { { 0, "" }, { 1, "" }, { 2, "" }, { 3, "" } };

var nums = dict.Keys.ToList();

Parallel.ForEach(nums, num =>
            {
                dict[num] = LongTaskToGenerateString();
            });

return dict;

3 个答案:

答案 0 :(得分:5)

不,Dictionary<TKey, TValue>类不是线程安全的修改,如documentation中所示:

  

只要未修改集合,Dictionary<TKey, TValue>可以同时支持多个阅读器。即便如此,通过集合枚举本质上不是一个线程安全的过程。在枚举与写访问争用的极少数情况下,必须在整个枚举期间锁定该集合。要允许多个线程访问集合以进行读写,您必须实现自己的同步。

在您的情况下,如果某些线程几乎同时完成LongTaskToGenerateString,那么它们的字典更新将会受到干扰。

您可以使用SyncRoot属性手动同步访问权限,也可以按照ConcurrentDictionary<TKey, TValue>在评论中的建议选择asawyer课程。

This实施表明,如果您只更新现有密钥的值,那么 应该(同时查看this) - 不优雅的效果可能是version属性的值不准确。它用于防止在枚举时修改集合,因此它最终会达到什么值并不重要。但是不知道对此有任何保证。

答案 1 :(得分:3)

好像您的词典仅用作返回值,并且实际上从未用于查找键。在这种情况下,在计算完所有最终输出值之前,您甚至不需要字典。

所以你可以使用PLINQ:

var nums = new[] { 0, 1, 2, 3 };

var dict = nums.AsParallel()
               .Select(num => new KeyValuePair<int, string>
                                  (num, LongTaskToGenerateString(num)));
               .ToDictionary(kvp => kvp.Key, kvp => kvp.Value);

return dict;

答案 2 :(得分:0)

要保证线程安全,请使用ConcurrentDictionary