List.AddRange() 线程安全吗?
我可以在不锁定的情况下从多个线程安全地调用 List.AddRange(r) 吗?如果不是的话,我会遇到什么样的麻烦呢?
Can I, without locking, safely call List.AddRange(r) from multiple threads? If not, what sort of trouble would I run into?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(5)
否,其文档没有说明它是线程安全的,因此它不是。
至于会出现什么问题,想一下 AddRange(newItems) 的作用:
与对 AddRange() 的另一个调用混合在一起,甚至只是与读取一个项目的调用混合在一起,会发生什么。
No, its documentation does not say it is thread safe, therefore it is not.
As to what can go wrong, think about what AddRange(newItems) does:
Now think what will happen if the above is mixed up with another call to AddRange() or even just a call to read an item.
不,不是,但我想补充一点,在锁内执行
myList.AddRange(...);
比执行多个lock (syncLock) { myList.Add 更有效(...) };
。你会遇到什么样的麻烦?当一个线程添加一项而另一个线程枚举列表时,
List
将抛出特定异常,因为它会进行一些内部版本控制,因为它希望防止我们可怜的开发人员遇到令人讨厌的副作用。此外,
List
在内部保留一个用于存储其项目的数组。也许在数组中设置一项是非常原子的,但是每当达到该数组的容量时,就会创建一个新数组,并且将从旧数组中复制这些项。因此,当线程想要在复制发生时添加某些内容时,您可以想象事情会不同步。No it's not, but I'd like to add it's more efficient to do an
myList.AddRange(...);
within a lock than doing severallock (syncLock) { myList.Add(...) };
.Which sort of trouble would you run into? When one thread is adding an item while another is enumerating the list,
List<T>
will throw a certain exception because it does some internal versioning, as it wants to prevent us poor developers from hitting nasty side effects.Also the
List<T>
internally keeps an array in which it stores its items. Maybe setting an item in an array is pretty atomic, but whenever the capacity of this array is reached, a new one will be created and the items will be copied over from the old one. So when a thread wants to add something while that copying takes place, you can imagine that things would go out of sync.根据您的使用情况,SynchronizedCollection 可能会起作用。
不过,您没有单次
AddRange
。如果您仅使用它来为集合播种,则可以执行此操作,因为存在IEnumerable
构造函数重载。Depending on your usage, SynchronizedCollection could work.
You'd have no single-shot
AddRange
though. If you are only using this to seed the collection, you can do this as there is anIEnumerable
constructor overload.在 .NET Framework 4.0 之前,没有 .NET 集合是线程安全的。然后,您需要先锁定它,然后才能在代码中访问它集合和同步(线程安全)
。另一方面,.NET Framework 4.0 引入了新的
System.Collections.Concurrent
命名空间,其中包括细粒度线程安全集合
。最后,如果您可以使用.NET Framework 4.0,我强烈建议您根据需要这样做,否则,请确保每次要修改或访问集合时都锁定该集合。
此外,静态集合应该是线程安全的,但要小心,因为不能保证成员是线程安全的。
编辑 #1
经过 Steve Townsend 评论的进一步验证,我承认从版本 3.0 开始,.NET Framework 中存在三个线程安全集合
我很抱歉,我自己才知道他们的存在。 =)
Until .NET Framework 4.0, no .NET collections are thread-safe. You will then be required to lock it before you access it in your codeCollections and Synchronization (Thread Safety)
.On the other hand, .NET Framework 4.0 introduces the new
System.Collections.Concurrent
namespace which includes fine-grainedThread-Safe Collections
.Finally, if you can use .NET Framework 4.0, I strongly recommend you do so for what you need, otherwise, make sure to lock the collection each time you want to modify or access it.
Besides, a static collection should be thread-safe, but beware, as the members are not guaranteed to be.
EDIT #1
After further verifications due to Steve Townsend's comment, I admit that there are three thread-safe collections within the .NET Framework starting with version 3.0:
I apologize, I just learned their exitense myself. =)
不,它不是线程安全的。
线程 A 可以调用您的列表上的 AddRange。它可以部分地迭代集合并切换线程。
线程 B 可以在线程 A 完成之前调用添加/删除等。
No it is not thread-safe.
Thread A could call AddRange on your list. It could iterate partially over the collection and switch threads.
Thread B could call Add/Remove, etc. before Thread A has finished.