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

lis*_*tor 7 hash multithreading semaphore push raku

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

Jon*_*ton 10

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

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

  • 内存性能严重依赖缓存
  • 某些缓存级别是每个 CPU 内核
  • 写入内存意味着将它排他性地放入当前核心的缓存中
  • 将它从一个内核的缓存中移动以写入它的过程成本很高(球包 60-100 周期损失)

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

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

  1. 将工作分解为每个工人的一部分
  2. 用于start触发每个工人,它返回一个Promise. 将Promises 放入数组中。
  3. 让每个Promise返回它产生的项目的数组或散列。
  4. 合并每个结果。例如,如果每个都返回一个数组,那么my @all-results = flat await @promises;或类似的就足以将所有结果收集在一起。

您可能会发现您的问题非常适合并行迭代器范式,使用 hyper 或 Race,在这种情况下,您甚至不需要自己分解工作或设置工作人员;相反,您可以选择程度和批量大小。