C#两个Thread操作同时一个List的问题

博客园铁粉 2017-03-23 12:10:59
有一个列表List<int> list;一个线程专门为List添加项,while(true) { list.Add(1); };一个线程专门删除List中的第一项,while(true)
{ if(list.Count>0) { list.RemoveAt(0); } }。
不加锁的情况下,在删除线程中已经做了Count判断,但是RemoveAt的时候会报“索引超出了数组界限”,请问为什么?
...全文
2201 53 打赏 收藏 转发到动态 举报
写回复
用AI写文章
53 条回复
切换为时间正序
请发表友善的回复…
发表回复
qq_18894259 2018-05-25
  • 打赏
  • 举报
回复
引用 44 楼 h2041075 的回复:
[quote=引用 40 楼 wanghui0380 的回复:] ConcurrentQueue<T>
这个没有Add和Remove方法啊[/quote] ConcurrentQueue的Enqueue()方法类似List的Add()
博客园铁粉 2017-03-27
  • 打赏
  • 举报
回复
引用 48 楼 xuzuning 的回复:
对的 ConcurrentQueue 是线程安全的,Queue 不是
好的明白了谢谢。
xuzuning 2017-03-27
  • 打赏
  • 举报
回复
对的 ConcurrentQueue 是线程安全的,Queue 不是
博客园铁粉 2017-03-27
  • 打赏
  • 举报
回复
引用 46 楼 xuzuning 的回复:
Queue 是 FIFO 队列 Dequeue 方法,移除并返回位于 Queue 开始处的元素。 等价于 a = List[0]; List.RemoveAt(0); Enqueue 方法,向 Query 添加一个元素 等价于 List.Add Clear 方法,清空队列 等价于 List.RemoveAll
刚刚试了一下,使用Queue在两个线程中一个Enqueue一个Dequeue会报错,换成ConcurrentQueue使用TryDequeue就不会报错了。
xuzuning 2017-03-27
  • 打赏
  • 举报
回复
Queue 是 FIFO 队列 Dequeue 方法,移除并返回位于 Queue 开始处的元素。 等价于 a = List[0]; List.RemoveAt(0); Enqueue 方法,向 Query 添加一个元素 等价于 List.Add Clear 方法,清空队列 等价于 List.RemoveAll
博客园铁粉 2017-03-27
  • 打赏
  • 举报
回复
引用 43 楼 xuzuning 的回复:
其实本该用 Queue 的
这个没有Remove方法啊
博客园铁粉 2017-03-27
  • 打赏
  • 举报
回复
引用 40 楼 wanghui0380 的回复:
ConcurrentQueue<T>
这个没有Add和Remove方法啊
博客园铁粉 2017-03-27
  • 打赏
  • 举报
回复
引用 50 楼 trh138320 的回复:
[quote=引用 49 楼 h2041075 的回复:] [quote=引用 48 楼 xuzuning 的回复:] 对的 ConcurrentQueue 是线程安全的,Queue 不是
好的明白了谢谢。[/quote] 我来说一下浅表的看法吧,但有可能是问题所在。 这个也是我偶然看到关于List一些实现原理发现的 List的Add操作 其实是基于一个数组,当add操作加入的元素数大于当前数组时,会重新new 一个数组,大小是原来的2倍。 那么问题来了,在新数组刚初始化,还未Copy元素时,此时新数组为空,remove操作自然会报错 但是此为设想,因为我基于楼主的代码,多次调试发现每次报错的时候数组大小不一致,可能跟当时的电脑使用环境有关,故此假设,若有误莫怪[/quote] 这个不难理解吧。 在新数组刚初始化,还未Copy元素时,此时新数组为空,设定这个时候为过度状态。 报错的原因就是在这个过度状态的时候,列表中没有任何东西,但此时却执行了Remove操作。而这个过度状态是由于两个线程不停切换导致的,你每次运行肯定不能保证两个线程切换得出的结果一模一样,甚至如果一模一样才会奇怪吧。
trh138320 2017-03-27
  • 打赏
  • 举报
回复
引用 50 楼 trh138320 的回复:
[quote=引用 49 楼 h2041075 的回复:] [quote=引用 48 楼 xuzuning 的回复:] 对的 ConcurrentQueue 是线程安全的,Queue 不是
好的明白了谢谢。[/quote] 我来说一下浅表的看法吧,但有可能是问题所在。 这个也是我偶然看到关于List一些实现原理发现的 List的Add操作 其实是基于一个数组,当add操作加入的元素数大于当前数组时,会重新new 一个数组,大小是原来的2倍。 那么问题来了,在新数组刚初始化,还未Copy元素时,此时新数组为空,remove操作自然会报错 但是此为设想,因为我基于楼主的代码,多次调试发现每次报错的时候数组大小不一致,可能跟当时的电脑使用环境有关,故此假设,若有误莫怪[/quote] 楼上的大神们给出的另外解决方案很好,但为楼主的思考问题方式点赞,有问题不仅需要解决方案,还应思考问题本质,不能做个只知道该怎么做而不知为什么这么做的程序员
trh138320 2017-03-27
  • 打赏
  • 举报
回复
引用 49 楼 h2041075 的回复:
[quote=引用 48 楼 xuzuning 的回复:] 对的 ConcurrentQueue 是线程安全的,Queue 不是
好的明白了谢谢。[/quote] 我来说一下浅表的看法吧,但有可能是问题所在。 这个也是我偶然看到关于List一些实现原理发现的 List的Add操作 其实是基于一个数组,当add操作加入的元素数大于当前数组时,会重新new 一个数组,大小是原来的2倍。 那么问题来了,在新数组刚初始化,还未Copy元素时,此时新数组为空,remove操作自然会报错 但是此为设想,因为我基于楼主的代码,多次调试发现每次报错的时候数组大小不一致,可能跟当时的电脑使用环境有关,故此假设,若有误莫怪
X_小狼 2017-03-24
  • 打赏
  • 举报
回复
引用 31 楼 sp1234 的回复:
实际上即使“幸好”程序不抛出异常,RemoveAt 语句最后的
this._items[this._size] = default(T);
这条语句可能也会去给错误位置的单元赋值,而原来应该重新初始化的那个单元,还在引用这不该引用的对象。造成内存泄漏。 这就只有经常写测试程序的人才能及时发现了,而不写测试的人可能就总是在系统维护中去猜测了。
还是老司机给力啊。。
wanghui0380 2017-03-24
  • 打赏
  • 举报
回复
ConcurrentQueue<T>
一团火 2017-03-24
  • 打赏
  • 举报
回复
我是一脸的蒙 完完全全不懂
xuzuning 2017-03-24
  • 打赏
  • 举报
回复
其实本该用 Queue 的
Hertz_liu 2017-03-24
  • 打赏
  • 举报
回复
List非线程安全的,hashtable是线程安全的
博客园铁粉 2017-03-23
  • 打赏
  • 举报
回复
引用 1 楼 qq_34798533 的回复:
判断完之后,删除之前程序在做什么
你说删除的那个线程吗?那个线程只是判断一下然后RemoveAt就没有别的了,期间另一个线程在往里面Add。
xuggzu 2017-03-23
  • 打赏
  • 举报
回复
就是因为你没加锁啊……
xiaoyu5425 2017-03-23
  • 打赏
  • 举报
回复
判断完之后,删除之前程序在做什么
john_QQ:2335298917 2017-03-23
  • 打赏
  • 举报
回复
这个问题问得很好,看了大神们的回答,也让我茅塞顿开,学习了
xuzuning 2017-03-23
  • 打赏
  • 举报
回复
其实看看 List<T> 的源码( https://referencesource.microsoft.com/#mscorlib/system/collections/generic/list.cs,cf7f4095e4de7646)就知道了,List<T> 并不是线程安全的
加载更多回复(33)

110,533

社区成员

发帖
与我相关
我的任务
社区描述
.NET技术 C#
社区管理员
  • C#
  • Web++
  • by_封爱
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告

让您成为最强悍的C#开发者

试试用AI创作助手写篇文章吧