我需要通过从外部服务器获取图像来改进构建布料外观的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)
答案 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)