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


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

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

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

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

4 63

4 ответа:

вы можете перечислить в группах по 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 утра, и у меня было только несколько часов сна. :/

Если я правильно вас понял, вы хотите иметь не более 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 }

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

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