5

下面的代码线程安全吗?

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

var nums = dict.Keys.ToList();

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

return dict;
4

3 回答 3

5

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

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

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

您可以按照asawyer在评论中的建议使用该SyncRoot属性手动同步访问,或者只参加课程。ConcurrentDictionary<TKey, TValue>

这个实现表明,如果你只更新现有键的值,它应该没问题(也看看这个) - 不优雅的效果可能是version属性的值不准确。它用于防止在枚举时修改集合,因此它最终的值并不重要。不过,不知道对此有任何保证。

于 2013-10-30T15:13:02.983 回答
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;
于 2013-10-30T15:30:17.247 回答
0

为了线程安全,请使用ConcurrentDictionary

于 2013-10-30T15:14:19.210 回答