Ruby线程上的重复结果

时间:2013-04-23 15:59:01

标签: ruby-on-rails ruby multithreading

我需要通过从外部服务器获取图像来改进构建布料外观的rake任务。

当我尝试创建多个线程时,结果会重复。

但如果我在每个sleep 0.1之前放置Thread.new,代码就可以了!为什么呢?

new_looks = []
threads = []

for look in looks
  # sleep 0.1 - when I put it, works!
  threads << Thread.new do
    # a external http request is being done here
    new_looks << Look.new(ref: look["look_ref"])
  end
end

puts  'waiting threads to finish...'
threads.each(&:join)

puts  'saving...'
new_looks.sort_by(&:ref).each(&:save)

2 个答案:

答案 0 :(得分:3)

数组通常不是线程安全的。切换到线程安全的数据结构,例如Queue:

new_look_queue = Queue.new
threads = looks.map do |look|
  Thread.new do
    new_look_queue.enq Look.new(ref: look["look_ref"])
  end
end

puts  'waiting threads to finish...'
threads.each(&:join)

puts  'saving...'
new_looks = []
while !new_look_queue.empty?
  new_look_queue << queue.deq
end
new_looks.sort_by(&:ref).each(&:save)

队列#enq 在队列中放入一个新条目; 队列#deq 获得一个,如果没有,则阻止。

如果您不需要按顺序保存new_looks,则代码会变得更简单:

puts 'saving...'
while !new_look_queue.empty?
  new_look_queue.deq.save
end

或者,甚至更简单,只需在线程中进行保存即可。


如果你有很多外观,上面的代码将创建更多的线程而不是好的。线程太多会导致请求处理时间过长,并消耗过多的内存。在这种情况下,请考虑创建一些生成器线程:

NUM_THREADS = 8

和以前一样,有一个完成工作的队列:

new_look_queue = Queue.new

但现在还有一系列工作要做:

look_queue = Queue.new
looks.each do |look|
  look_queue.enq look
end

每个线程都会一直运行直到它失去工作,所以让我们在队列中添加一些“失去工作”的符号,每个线程一个:

NUM_THREADS.times do {look_queue.enq :done}

现在是线程:

threads = NUM_THREADS.times.map do
  Thread.new do
    while (look = look_queue.deq) != :done
      new_look_queue.enq Look.new(ref: look["look_ref"])
    end
  end
end

处理new_look_queue与上面相同。

答案 1 :(得分:1)

尝试将您的代码更新为此代码:

for look in looks
  threads << Thread.new(look) do |lk|
    new_looks << Look.new(ref: lk["look_ref"])
  end
end

这应该对你有帮助。

UPD:忘了关于Thread.new(args)