红宝石在四个一组的数组元素上工作


78

当每个元素都需要处理时,我有一个ruby脚本数组:

threads = []
elemets.each do  |element|
    threads.push(Thread.new{process(element)}}
end
threads.each { |aThread|  aThread.join }

但是由于资源限制,如果一次不处理四个元素,脚本将以最佳方式工作。

不,我知道我可以转储每个循环并使用一个变量来计数4个元素,然后等待,但是有没有更凉爽的红宝石方法呢?

Answers:


164

您可以按4个一组枚举数组:

>> [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12].each_slice(4) {|a| p a}
[1, 2, 3, 4]
[5, 6, 7, 8]
[9, 10, 11, 12]

所以你可以尝试像

elements.each_slice(4) do | batch |
    batch.each do | element |
        threads.push(Thread.new{process(element)}}

    end
    (do stuff to check to see if the threads are done, otherwise wait )
end

不过,它可能并不是您所需要的-自凌晨3点起,我已经起床,而且我只有几个小时的睡眠时间。:/


2
@Rilindo:这太棒了!修改了两行,我很好。谢谢。
伊莱(Eli)2010年

2
当任务需要花费可变的时间来处理时,下面的(我)解决方案应该更有效。此解决方案假定每个线程将花费相同的时间来处理4个元素的列表。
安德鲁·库克里维奇

2
我想我只是再一次爱上了Ruby :)
superluminary 2012年

如果您使用的是Rails,则还有更多可读的“ in_groups_of” ... elements.in_groups_of(4)| group | 等等
Jason

21

如果我没看错,您一次最多只能处理4个线程。

在我看来,您应该只启动4个线程,并从共享的Queue(标准线程库的一部分)中读取所有线程以处理元素。

当队列为空时,您可以使线程结束。

将数组切成4个相等的数组,并让每个线程处理1/4个元素,并假定每个元素在同一时间处理。如果某些线程比其他线程花费更长的时间,则您的某些线程将提早完成。

使用队列,直到共享队列为空,没有线程停止,因此我认为这是一种更有效的解决方案。

这是一个基于您的代码的工作程序,用于演示:

require 'thread'

elements = [1,2,3,4,5,6,7,8,9,10]

def process(element)
    puts "working on #{element}"
    sleep rand * 10
end

queue = Queue.new
elements.each{|e| queue << e }

threads = []
4.times do
    threads << Thread.new do
      while (e = queue.pop(true) rescue nil)
        process(e)
      end
    end
end

threads.each {|t| t.join }

该解决方案对我来说非常接近完美,除了它在完成时会引发错误:ArgumentError: tried to create Proc object without a block似乎不喜欢while (e = queue.pop(true) rescue nil)
SaltedBlowfish

我没有收到该错误,尝试了2个版本的ruby-您使用的是哪个版本?
安德鲁·库克里维奇

版本号2.3.1。我在Rails的rake任务中运行它,因此很可能在其他地方发生冲突。
SaltedBlowfish

2

不知道以下变体是否算作仅使用“可计算4个元素的变量”,还是可以认为很酷,但是它为您提供了一个数组,其大小不超过4个元素:

x = (1..10).to_a
0.step(x.size - 1, 4) do |i|
    # Choose one
    p x.slice(i, 4)
    p x[i, 4]
end

2

在铁轨中可以使用更易读的形式 in_groups_of

arr= [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]
arr.in_groups_of(4, false) {|a| p a}

结果:

[1, 2, 3, 4]
[5, 6, 7, 8]
[9, 10, 11]

最后一行只有3个元素,因为我们在中指定了false in_group_of。如果需要nil或其他任何值,则可以用该值替换false。


in_groups_of是一种rails方法,不适用于普通红宝石
Subash

1

是的,但是您需要做一些方法重写。通常的方法是这样覆盖'/' Array

class Array
  def / len
    a = []
    each_with_index do |x,i|
      a << [] if i % len == 0
      a.last << x
    end
    a
  end
end 

有了这个定义,您现在可以轻松地执行以下操作:

foo = [1,2,3,4,5,6]
foo / 2
# Result is [[1,2], [3,4], [5,6]]

2
我认为在此类基本类上重写方法非常危险-即使(例如在这种情况下)之前未定义它们。为什么/%?如果另一位开发人员(或实施此操作的我)在一年或两年之内到来并想弄清楚代码,问“Array数字除以数字到底意味着什么”怎么办?
haslo 2015年
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.