2

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

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

但是,如果我sleep 0.1在 each 之前放置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)
4

2 回答 2

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)

Queue#enq将新条目放入队列;Queue#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 同上。

于 2013-04-23T16:28:27.417 回答
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)

于 2013-04-23T16:06:25.983 回答