ConcurrentQueue的ToList()与ToArray().ToList()对比:重构是否安全?
关于ConcurrentQueue.ToList()是否生成副本及重构安全性的解答
好问题!咱们把这个问题拆解清楚:
首先,你已经知道ConcurrentQueue<T>.ToArray()会生成队列当前状态的线程安全快照副本——也就是说,调用这个方法时,会把队列里的所有元素复制到一个新数组里,后续其他线程对队列的修改不会影响这个副本。
那直接调用ToList()呢?答案是:同样会生成线程安全的快照副本,把代码从A重构为B是完全安全的。
为什么会这样?
这要从ToList()扩展方法(来自System.Linq.Enumerable)的实现逻辑说起:
- 当调用
IEnumerable<T>.ToList()时,如果目标对象实现了ICollection<T>(而ConcurrentQueue<T>确实实现了这个接口),方法会直接利用ICollection<T>的Count属性和CopyTo方法来高效创建列表。 ConcurrentQueue<T>的CopyTo方法和ToArray()的行为完全一致:它会获取队列的当前快照,把所有元素复制到目标容器(这里就是List<T>的内部数组),整个过程是线程安全的,不会被其他线程的入队/出队操作干扰。
额外补充:代码A和代码B的差异
代码A是queue.ToArray().ToList(),相当于先把队列复制到一个数组,再把数组转成列表——这多了一次数组的分配和复制操作;而代码B直接调用ToList(),内部会直接把元素复制到List<T>的内部数组,效率略高,代码也更简洁。
总结一下:两种写法都会生成独立于原队列的副本,后续对原队列的修改不会影响listA或listB,所以重构是安全的。
内容的提问来源于stack exchange,提问作者Svek
相关产品推荐
相关产品推荐

