2012-10-25 12 views
5

Ich habe den folgenden Code (from a Ruby tutorial):Loops in mehreren Threads

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}" 

Es ein Beispiel ist Mutex.synchronize für die Verwendung. Auf meinem Computer unterscheiden sich die Ergebnisse ziemlich vom Lernprogramm. Nach join Aufruf sind die Zählungen manchmal gleich:

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

und manchmal nicht:

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

Ich verstehe nicht, wie es möglich ist, dass der Unterschied noch 0 ist, obwohl die Grafen sehr unterschiedlich zeigen Zahlen.

Der add Betrieb sieht wohl so aus:

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

und etwas ähnliches für count2. Ruby kann die Ausführung zwischen Threads wechseln, so dass es möglicherweise nicht fertig ist, in eine Variable zu schreiben, aber wenn die CPU zum Thread zurückkehrt, sollte sie von der Zeile weitergehen, in der sie unterbrochen wurde, richtig?

Und es gibt immer noch nur einen Thread, der in die Variable schreibt. Wie ist es möglich, dass innerhalb des loop do Blocks count2 += 1 viel öfter ausgeführt wird?

+0

was soll 'join (2)' tun? – uday

+0

gibt es dem Thread ein Limit (in Sekunden) zu beenden. Wenn ich das nicht rufe, wird Ruby automatisch Threads ernten, wenn das Ende des Programms erreicht wird (also endet unendliche 'loop do' immer). siehe http://www.ruby-doc.org/core-1.9.3/Thread.html#method-i-join für weitere Informationen – Tombart

+1

Das ist interessant. Bei Ruby 1.8 ist die "Differenz" immer <> 0 und die Anzahl unterscheidet sich nie um mehr als 1, aber bei Ruby 1.9 ist die "Differenz" immer == 0 aber count1 und count2 sind weit voneinander entfernt. – Casper

Antwort

3

Durchführung von

puts "count1 : #{count1}" 

dauert einige Zeit (obwohl es kann kurz sein). Es wird nicht in einer Instanz ausgeführt. Daher ist es nicht mysteriös, dass die zwei aufeinanderfolgenden Zeilen:

unterschiedliche Zählungen zeigen. Einfach ging der Thread counter durch einige Schleifenzyklen und inkrementierte die Zählungen, während der erste puts ausgeführt wurde. Ähnlich

, wenn

difference += (count1 - count2).abs 

berechnet wird, kann die Zählungen im Prinzip Schritt während count1 vor count2 verwiesen wird, wird verwiesen. Aber es gibt keinen Befehl, der innerhalb dieser Zeitspanne ausgeführt wird, und ich nehme an, dass die Zeit, die benötigt wird, um auf count1 zu verweisen, viel kürzer ist als die Zeit, die der Thread benötigt, um eine andere Schleife zu durchlaufen. Beachten Sie, dass die in ersterem durchgeführten Operationen eine echte Teilmenge dessen sind, was in letzterem getan wird. Wenn die Differenz signifikant genug ist, was bedeutet, dass der Thread counter während des Argumentaufrufs für die Methode - keinen Schleifenzyklus durchlaufen hat, werden count1 und count2 als derselbe Wert angezeigt.

wird eine Vorhersage, dass, wenn Sie count1 nach der Referenzierung einige teure Berechnung setzen aber vor count2 Referenzierung, dann wird difference up zeigen:

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

In der Tat Putting etwas wie "Schlaf 0,001" in den Counter-Thread zwischen den Zählern auch den Unterschied zeigen. Da der Counter-Thread "busy looping" ist, frage ich mich, ob der Spionage-Thread sogar eine Chance hat, in 1.9 zu laufen. Wenn Sie die Sleep-Anweisung sofort einfügen, wird die CPU-Zeit für die Ausführung des Spionage-Threads frei, und der Unterschied zeigt sich. – Casper

+1

Danke, es macht Sinn. Ersetzen von 'puts' durch' print "count1: # {count1}, count2: # {count2} \ n" 'könnte den Unterschied zwischen den Zählern verringern. Weil 'puts' als zwei Befehle ausgeführt wird und daher etwas mehr Zeit benötigt. – Tombart

0

Hier ist die Antwort. Ich denke, Sie haben die Annahme gemacht, dass die Threads die Ausführung stoppen, nachdem join(2) zurückkehrt.

Dies ist nicht der Fall! Die Threads werden weiterhin ausgeführt, obwohl join(2) die Ausführung (vorübergehend) an den Hauptthread zurückgibt.

Wenn Sie Ihren Code dies ändern, werden Sie sehen, was passiert:

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

counter.kill 
spy.kill 

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

Dieses ein wenig anders in Ruby zu arbeiten scheint 1.8, wo die Fäden scheinen, während der Haupt keine Chance zu bekommen zu laufen Thread wird ausgeführt.

Das Tutorial ist wahrscheinlich für Ruby 1.8 geschrieben, aber das Threading-Modell wurde seitdem in 1.9 geändert.

Tatsächlich war es "Glück", dass es in 1.8 funktionierte, da die Threads die Ausführung nicht beenden, wenn join(2) weder in 1.8 noch in 1.9 zurückkehrt.