在Ruby哈希中并行化键值对的创建问题

时间:2015-07-07 16:06:22

标签: ruby multithreading hash parallel-processing jruby

使用Ruby并使用Parallel和JRuby 1.7.19编写以下代码,以加速从具有多个值的数组创建哈希:

hash = {}
array = [
  {"id" => "A001", "value" => 1},
  {"id" => "B002", "value" => 0},
  {"id" => "C003", "value" => 3},
  {"id" => "D004", "value" => 0}]

Parallel.each(array, { in_threads: 5 }) do |item|
  if keep_item?(item)
    hash[item["id"]] = item
  end
end
def keep_item?(item)
  item["value"] > 0
end

我注意到在Ruby中并行添加哈希值的问题可能存在问题。这段代码是否存在任何风险(线程安全,数据丢失,我不知道的奇怪锁等),我应该把它作为常规系列#each调用?

2 个答案:

答案 0 :(得分:3)

Hash不是线程安全的。如果keep_item?访问hash,则会出现竞争状况。即使它没有,也会有hash的并发更新,这很容易出错。

如果没有锁定或其他同步,理论上不能保证在一个线程上对非线程安全hash的更新在其他线程上可见。没有同步的hash的并发更新可能会丢失数据,或导致其他奇怪的问题。这取决于Ruby Hash的实现。

您的数据很简单,只需使用普通each处理它们即可。如果使用Parallel并添加mutex/lock以进行线程安全访问,则同步开销将显着增加整个进程的额外时间成本。安全并行版本可能会占用更多时间。

Parallel在您的任务为IO限制时非常有用,或者只要您拥有可用内核并且任务不需要在彼此之间交换数据,CPU就会受到限制。

答案 1 :(得分:1)

虽然Arie Shaw对Hash不是线程安全是正确的,但我认为你的实现比任何事情更重要。如果您必须并行处理,我建议使用ndx = crossfilter(data); dataTable = dc.dataTable('#data-table'); var tableDim = ndx.dimension(function(d) { return d.gender + "/" + d.hair-color + "/" + d.pets; }); dataTable .width(400) .height(800) .dimension(tableDim) .group(function(d){ return "Data Counts"; }), .columns([ function(d) { return d.gender; }, function(d) { return d.hair-color; }, function(d) { return d.pets; } function(d) { if (d.group == 1) return d.totals; else return 0; }, function(d) { if (d.group == 2) return d.totals; else return 0; }, function(d) { if (d.group == 3) return d.totals; else return 0; 代替。

Parallel::map

请注意,这比直接在阵列上运行它要慢得多,例如。

 #lambda just to create the array structure
 lam = ->(n) {n.times.map {|i| {'id' => 'A' + i.to_s, 'value' => [i,0].shuffle.pop }}}
 a = lam.call(40_000)
 require 'parallel'
 Parallel.map(a,in_threads: 5) {|h| [h['id'],h] if h['value'] > 0}.compact.to_h

a.map {|h| [h['id'],h] if h['value'] > 0}.compact.to_h 结果:

fruity