哈希和数组的推送和弹出操作是原子的和线程安全的吗?

Are push and pop operation for hashes and arrays atomic and thread-safe?

我有一个巨大的数据文件(接近 4T)需要处理。我在我的 4 核 CPU 上使用 4 个线程。第一个线程分析文件的第一季度,依此类推。所有线程都需要在分析完自己数据文件四分之一的部分后,将它们的结果添加到相同的单个散列和单个数组中。那么,散列和数组的 "push" 和 "pop" 以及 "shift" 和 "unshift" 操作是原子的和线程安全的,还是我必须求助于信号量等更复杂的机制?

不,它们既不是原子的也不是线程安全的,多线程使用会导致崩溃或数据不一致。

就是说,即使是这样,当您添加更多线程时,涉及同一数据结构上大量争用的设计的扩展性也会很差。这是因为硬件在面对并行性时的工作方式。简要地:

  • 内存性能严重依赖于缓存
  • 一些缓存级别是每 CPU 个核心
  • 写入内存意味着将其独占到当前内核的缓存中
  • 将其从一个核心的缓存中移出以写入的过程代价高昂(ballpack 60-100 周期损失)

您可以使用锁定来获得正确性。为此,我不建议直接使用锁,而是查看像 OO::Monitors 这样的模块,您可以在其中将哈希封装在一个对象中并在边界处完成锁定。

如果您在共享数据结构上执行的推送次数与生成要推送的项目所做的工作量相比较少,那么您可能不会在围绕数据结构的锁定和争用方面遇到瓶颈。但是,如果您每秒执行数千次 pushes 或类似操作,我建议您寻找替代设计。例如:

  1. 将工作分解为每个工人的一部分
  2. start引爆每个工人,return是Promise。将 Promise 放入数组中。
  3. 为每个 Promise return 生成一个数组或哈希值。
  4. 合并每个结果。例如,如果每个 return 是一个数组,那么 my @all-results = flat await @promises; 或类似的就足以将所有结果聚集在一起。

您可能会发现您的问题很适合 parallel iterator paradigm, using hyper or race,在这种情况下您甚至不需要分解工作或自己设置工人;相反,您可以选择学位和批量大小。