ArrayLists and Vector comparison



在一个分配问题中,有人问它,当有多个线程访问和修改时,使用和ArrayList是否好。如果不好,做的最佳方法是什么

我知道ArrayLists没有同步,所以这意味着从性能角度来看,使用ArrayLists-是一个不错的选择

但由于多个线程修改和访问它,我认为数据的完整性可能不安全。因此,从这个角度来看,我认为矢量在考虑数据完整性时更合适,因为矢量是同步的。

我想知道我的建议是否正确。或者ArrayLists是否是上述场景中最好的

如果使用Vector,那么同步总是会有一些开销。ArrayList并不那么安全,但对于您的线程,您可以自己进行同步,例如使用同步对象或:

Collections.synchronizedList(new ArrayList<String>());

然后,在没有Vector开销的情况下,您仍然可以在执行线程任务后访问ArrayList。如果您只是在线程中使用该列表,我想Vector也是一个不错的选择。我尽量避开Vector。

我只在英特尔的线程构建块和微软的并行模式库中使用过concurrent_vector,但我认为它们可能与Java的Vector相当。

根据我的测试,两者中的concurrent_vector都比大多数替代方案慢得多,比如执行多个线程并将结果收集在本地线程不安全的容器中(比如C++中的std::vector),然后将结果附加到锁内的共享集合中,或者创建一个列表列表,每个线程都写入自己的列表(使用一些数组索引),然后以串行方式,最后将结果合并到一个列表中。

据我所知,线程安全版本的好处是方便。我从来没有找到过并发的、线程安全的随机访问序列,即使是在英特尔自己的库中,我也无法用线程不安全的替代方案来击败它。在这种替代方案中,我在一个线程中本地累积结果,然后使用一些基本的线程同步,并将结果以串行方式组合到一个线程。如果我的测试写得很重,那么使用这种粗糙的方法,我通常可以比并发容器更快地获得2-3倍的结果。

也就是说,在许多情况下,您的时间实际上偏向于向容器中写入/附加元素,这可能是罕见的。更多的时候,我发现我在现实世界中的大部分线程案例都花在了阅读和处理数字等方面,而只有一小部分时间花在了将结果推到容器后面。因此,通常情况下,并发容器的开销开始变得微不足道,而且它肯定更方便,也不太容易在线程间滥用。

在可能罕见的情况下,写入容器是并行算法中花费的大量时间,在我的测试和经验中,并发容器从未为粗糙的替代方案提供性能优势。因此,如果它是代码中性能特别关键的部分,并且您在分析会话中的并发容器的方法中看到热点,我会尝试在非并发的线程本地容器(即线程本地ArrayList)中累积输出,然后在算法结束时以串行方式从一个线程或在锁/关键部分内组合所有结果(例如:组合的ArrayList)。

不幸的是,要使事情既线程安全又具有最大可扩展性是很棘手的。只需在一个线程中执行所有内容,就可以在体系结构中最大限度地实现线程安全。线程安全解决!但这并不能充分利用并行性。我发现了类似的并发性——一种平衡行为,用头撞Amdahl定律。我发现并发容器处于中等位置,因为使用它们几乎总是在某种程度上牺牲最佳性能,但最佳和不完全最佳之间的差异可能可以忽略不计。好吧,你像我一样测量和观察。

关于你问题的这一部分:

但由于多个线程修改和访问它,我认为可能无法确保数据的完整性

从我的角度来看,这与设计有关。我已经好几年没有从理论角度思考计算机科学了,但这里有一个隐含的假设,即这些数据必须共享。根据用户端的要求,数据可能需要共享,也可能不需要共享。以从scene访问数据的视频游戏为例。渲染引擎和物理引擎等等似乎都必须共享同一个场景。这很直观。这是有人性的。

但事实并非如此。从用户端的角度来看,如果渲染器有自己的场景副本,用于将结果渲染到屏幕上,这可能并不重要,因为这可能与正在发生的其他事情稍微不同步。因此,有时,至少在需要最佳性能或帧速率或最短等待时间的情况下,在那里,您可以复制/复制数据,以允许线程以最快的速度运行,而无需访问共享数据所需的任何线程同步(包括排除原子操作)。无论是否使用持久数据结构等,这都可能变得非常复杂。这取决于设计。但我认为多线程编程的一个反直觉方面是,我们首先会认为需要在线程之间共享的东西比真正需要共享的东西更多,而放弃这种假设可以产生全新的并行度。我和我的许多同事以及我自己发现,拥有触手可及的并发容器会诱使我们在线程之间共享比实际需要的更多的数据。如果我们的目标是尽可能有效地利用硬件,那么尽可能多地放弃共享数据的概念通常是有意义的,包括这些并发容器。如果你的软件像游戏引擎一样对性能至关重要,我实际上建议尽量减少它们的使用。

最新更新