C# 多线程同时往一个Dictionary Add数据时产生数组越界问题的原因分析

C# 多线程同时往一个Dictionary Add数据时产生数组越界问题的原因分析

问题描述:

观察以下代码,多线程同时向一个Dictionary中Add数据,运行后发现报数组越界的错误

using System;
using System.Collections.Generic;
using System.Threading.Tasks;

namespace CSharpTest2
{
    class Program
    {
        public static void Main(string[] args)
        {

            Dictionary<int, int> map = new Dictionary<int, int>();
            List<Task> tasks = new List<Task>();   

            for(int i = 0; i < 100; i++)
            {
                int k = i;
                tasks.Add(Task.Run(() =>
                {
                    map.Add(k, k);
                }));
            }

            Task.WaitAll(tasks.ToArray());
            Console.WriteLine(tasks.Count);
        }
    }
}

错误产生原因:
要知道错误原因,就需要先了解Dictionary的Add方法是如何工作的。
简单的说:Dictionary是动态增长的,Dictionary的Add方法在工作时,会先检查Dictionary容量是否足够,如果容量足够,就放里放数据,也就是说,先检查,后添加。
但是,多线程工作时,可能产生这么一种情况,就是当Dictionary剩余容量只有一个时,多个线程同时检查,都会发现Dictionary容量足够,然后都往里放数据,结果造成数组越界。

我们再做一个实验
不是说容量不够引起了数组越界问题吗?那我创建Dictionary时指定足够的容量应该就能避免这样的问题吧
因此我们修改代码
//Dictionary<int, int> map = new Dictionary<int, int>();
Dictionary<int, int> map = new Dictionary<int, int>(1000);
再次运行时发现确实没有出现问题了,这证明了我们的观点

为了进一步理解错误产生原因,我们查看Dictionary源码

发现Add方法其实时这样的

public void Add(TKey key, TValue value) {
	Insert(key, value, true);
}

而Insert方法中存在以下代码

......
else {
    if (count == entries.Length)
    {
        Resize();
        targetBucket = hashCode % buckets.Length;
    }
    index = count;
    count++;
}
entries[index].hashCode = hashCode;
entries[index].next = buckets[targetBucket];
entries[index].key = key;
entries[index].value = value;
......

其中
if (count == entries.Length)
{
Resize();
targetBucket = hashCode % buckets.Length;
}
就是在判断Dictionary容量是否足够,如果容量不够则扩容


entries[index].hashCode = hashCode;
entries[index].next = buckets[targetBucket];
entries[index].key = key;
entries[index].value = value;
就是将数据存储进Dictionary

显然,这样的先检查后添加的逻辑,在多线程同时往一个Dictionary Add数据时,完全可能造成“容量足够”的误判

参考文章
【C#】浅析C# Dictionary实现原理

  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值