2012-10-25 57 views
5

我有以下代碼(from a Ruby tutorial):循環在多線程

require 'thread' 

count1 = count2 = 0 
difference = 0 
counter = Thread.new do 
    loop do 
     count1 += 1 
     count2 += 1 
    end 
end 
spy = Thread.new do 
    loop do 
     difference += (count1 - count2).abs 
    end 
end 
sleep 1 

puts "count1 : #{count1}" 
puts "count2 : #{count2}" 
puts "difference : #{difference}" 
counter.join(2) 
spy.join(2) 
puts "count1 : #{count1}" 
puts "count2 : #{count2}" 
puts "difference : #{difference}" 

這是用於使用Mutex.synchronize一個例子。在我的電腦上,結果與教程完全不同。調用join後,計數有時等於:

count1 : 5321211 
count2 : 6812638 
difference : 0 
count1 : 27307724 
count2 : 27307724 
difference : 0 

有時不是:

count1 : 4456390 
count2 : 5981589 
difference : 0 
count1 : 25887977 
count2 : 28204117 
difference : 0 

我不明白它是如何可能的差異仍然0即使計數顯示出非常不同數字。

add操作可能是這樣的:

val = fetch_current(count1) 
add 1 to val 
store val back into count1 

count2類似的東西。 Ruby可以在線程之間切換執行,所以它可能不會完成寫入變量,但是當CPU回到線程時,它應該從中斷線繼續,對吧?

而且仍然只有一個線程寫入變量。在loop do塊中,count2 += 1被執行了多少次,這怎麼可能?

+0

'join(2)'應該做什麼? – uday

+0

它給線程一個限制(以秒爲單位)終止。如果我不會調用它,ruby會在程序結束時自動收集線程(所以無限循環會永遠結束)。請參閱http://www.ruby-doc.org/core-1.9.3/Thread.html#method-i-join瞭解更多信息 – Tombart

+1

這很有趣。在ruby 1.8上,'difference'總是<> 0,並且計數永遠不會超過1,但在ruby 1.9上,'difference'總是== 0,但是count1和count2彼此相距很遠。 – Casper

回答

3

puts "count1 : #{count1}" 

執行需要一定的時間(儘管它可以是短的)。這不是在一個實例中完成的。因此,連續兩條線不是神祕的:

puts "count1 : #{count1}" 
puts "count2 : #{count2}" 

顯示不同的計數。簡單地說,counter線程執行了一些循環週期並增加了計數,而第一個puts被執行。

同樣,當計算

difference += (count1 - count2).abs 

,計數可能會在原則上增量而count1count2之前引用的參考。但是在這段時間內沒有執行任何命令,我想參考count1的時間比counter線程通過另一個循環所花費的時間要短得多。請注意,在前者中完成的操作是後者中所做操作的適當子集。如果差異足夠大,這意味着counter線程在-方法的參數調用期間未經歷循環循環,則count1count2將顯示爲相同的值。

的預測將是,如果你把一些昂貴的計算參考count1之後,但引用count2前,然後difference會顯示:

difference += (count1.tap{some_expensive_calculation} - count2).abs 
# => larger `difference` 
+0

實際上,在計數器線程之間放置類似'sleep 0.001'的東西也會使差異顯現出來。由於計數器線程「忙碌循環」,我不知道間諜線程是否有機會在1.9中運行。毫不費力地將睡眠語句釋放,以釋放CPU時間來運行間諜線程,並顯示差異。 – Casper

+1

謝謝,這是有道理的。用'print'替換'puts' count1:#{count1},count2:#{count2} \ n「'可以減少計數器之間的差異。因爲'puts'是作爲兩個命令執行的,因此需要更多時間。 – Tombart

0

這裏的答案。我想你已經假設線程在join(2)返回後停止執行。

事實並非如此!即使join(2)將執行(暫時)返回到主線程,線程仍會繼續運行。

如果你改變你的代碼,這樣你將看看會發生什麼:

... 
counter.join(2) 
spy.join(2) 

counter.kill 
spy.kill 

puts "count1 : #{count1}" 
puts "count2 : #{count2}" 
puts "difference : #{difference}" 

這似乎在紅寶石1.8,不同的工作位,其中螺紋似乎並沒有得到一個機會,而主運行線程正在執行。

本教程可能是爲ruby 1.8編寫的,但自1.9版本開始,線程模型已經改變。

事實上,它在1.8中工作純粹是「運氣」,因爲線程在join(2)既不返回1.8也不返回1.9時沒有完成執行。