字典更新的线程安全性

6
以下代码是线程安全的吗?
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
经验法则:如果你不得不问,那就不是。;-) - user395760
3个回答

6
不,`Dictionary` 类不支持修改时的线程安全,如 文档 所述:

Dictionary 可以同时支持多个读取器,只要集合没有被修改。即便如此,枚举整个集合本质上也不是线程安全的过程。在罕见情况下,当枚举与写访问发生争用时,必须在整个枚举期间锁定集合。要允许多个线程对集合进行读取和写入访问,必须实现自己的同步。

在您的情况下,如果一些线程几乎同时完成了 LongTaskToGenerateString,它们的字典更新将会相互干扰。

你可以使用SyncRoot属性手动同步访问,或者像评论中建议的那样,使用ConcurrentDictionary<TKey, TValue>类。建议由asawyer提供。

这个实现表明,如果你只更新现有键的值,那么它应该没问题(同时也看这里)- 不太优雅的影响可能是version属性的值不准确。它用于防止在枚举集合时修改它,因此它实际上并不重要它将以哪个值结束。不知道关于这方面有什么保证。


4

看起来你的字典只用作返回值,实际上从未用于查找键。在这种情况下,直到计算出所有最终输出值之前,你甚至不需要使用字典。

因此,你可以使用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;

0

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接