Ruby работает над элементами массива в группах по четыре

У меня есть массив ruby ​​ script, когда каждый элемент нуждается в обработке:

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

как бы из-за ограничений ресурсов, script работает оптимальным образом, если не более четырех элементов обрабатываются за раз.

нет. Я знаю, что могу сбрасывать каждый цикл и использовать переменную для подсчета 4 элементов, а затем ждать но есть ли более холодный рубиновый способ сделать это?

Ответ 1

Вы можете перечислять в группах по 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

Если я правильно прочитаю, вы хотите иметь не более 4 потоков обработки за раз.

Мне кажется, что вы должны запускать только 4 потока, и все они читаются из общей очереди (часть стандартного потока lib) для обработки элементов.

У вас могут быть потоки завершены, когда очередь пуста.

Нарезание массива на 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 }

Ответ 3

Не уверен, что следующий вариант учитывается как просто использование "переменной для подсчета 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

Ответ 4

Да, но вам нужно переопределить метод. Обычный подход состоит в том, чтобы переопределить '/' для 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]]

Ответ 5

В рельсах можно использовать более читаемую форму 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]