2013-04-23 32 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