2013-04-23 26 views
2

我需要通过从外部服务器获取图像来改进构建布料外观的耙子任务。Ruby线程上的重复结果

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

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

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) 

回答

3

阵列一般不线程安全的。切换到一个线程安全数据结构如队列:

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

尝试更新您的代码以这一个:

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

这会帮助你。

UPD:忘了Thread.new(参数)

+0

我已更新我的示例 – 2013-04-23 16:16:09

+0

您能否解释有关参数?我找不到Thread的构造函数的文档。 – 2013-04-23 18:25:05

+0

我知道一些教程中的Thread.new参数。我不知道为什么,但没有关于Ruby 1.9.3中Thread类的'initialize'方法的文档,但是您可以在这里找到一些参数:http://ruby-doc.org/core-2.0/ Thread.html#方法-C-新 – 2013-04-23 18:51:59