Die Programmiersprache Ruby (German Edition)
um. Die Standard-C-Implementierung von Ruby 1.8 nutzt zum Beispiel nur einen einzelnen echten Thread und führt alle Ruby-Threads innerhalb dieses einen echten Threads aus. Das bedeutet, dass Threads in Ruby 1.8 sehr leichtgewichtig sind, aber auch nie parallel laufen, selbst auf CPUs mit mehreren Kernen.
Ruby 1.9 ist anders: Es alloziert einen echten Thread für jeden Ruby-Thread. Aber da manche der C-Bibliotheken in dieser Implementierung nicht selber Thread-sicher sind, ist Ruby 1.9 sehr konservativ und erlaubt niemals, dass mehr als einer seiner nativen Threads gleichzeitig läuft. (Diese Einschränkung kann in späteren Releases von 1.9 etwas gelockert werden, wenn der C-Code Thread-sicher gemacht werden kann.)
JRuby, die Java-Implementierung von Ruby, bildet jeden Ruby-Thread auf einen Java-Thread ab. Aber die Implementierung und das Verhalten von Java-Threads hängen wiederum von der Implementierung der Java Virtual Machine ab. Moderne Java-Implementierungen implementieren Java-Threads typischerweise als native Threads und ermöglichen eine echte parallele Verarbeitung auf CPUs mit mehreren Kernen.
----
9.9.1 Thread-Lebenszyklus
Wie oben beschrieben wurde, werden neue Threads mit
Thread.new
erzeugt. Sie können auch die Synonyme
Thread.start
und
Thread.fork
verwenden. Es ist nicht notwendig, einen Thread zu starten, nachdem man ihn erzeugt hat – er läuft automatisch los, wenn die CPU für ihn Zeit hat. Der Wert des Aufrufs von
Thread.new
ist ein
Thread
-Objekt. Die Klasse
Thread
definiert eine Reihe von Methoden zum Abfragen und Verändern des Threads, während er läuft.
Ein Thread führt denjenigen Code im Block aus, der mit dem Aufruf von
Thread.new
verbunden ist, und beendet dann seine Ausführung. Der Wert des letzten Ausdrucks in diesem Block ist der Wert des Threads, und man kann auf ihn zugreifen, indem man die Methode
value
des
Thread
-Objekts aufruft. Wenn der Thread schon fertig ist, liefert
value
den Wert des Threads direkt zurück. Ansonsten wartet die Methode
value
und kehrt erst zurück, wenn der Thread fertig ist.
Die Klassenmethode
Thread.current
liefert das
Thread
-Objekt zurück, das für den aktuellen Thread steht. Damit können sich Threads selbst bearbeiten. Die Klassenmethode
Thread.main
gibt das
Thread
-Objekt zurück, das für den Haupt-Thread steht – also den anfänglichen Thread, dessen Ausführung begann, als das Ruby-Programm gestartet wurde.
9.9.1.1 Der Haupt-Thread
Der Haupt-Thread ist etwas Besonderes: Der Ruby-Interpreter beendet seine Verarbeitung, wenn der Haupt-Thread fertig ist. Das geschieht selbst dann, wenn der Haupt-Thread andere Threads erzeugt hat, die immer noch laufen. Sie müssen daher sicherstellen, dass Ihr Haupt-Thread nicht endet, solange andere Threads noch laufen. Eine Möglichkeit, das zu erreichen, ist, Ihren Haupt-Thread in Form einer Endlosschleife zu schreiben. Eine andere Möglichkeit ist, explizit darauf zu warten, dass die anderen Threads beendet sind. Wir haben schon erwähnt, dass Sie die Methode
value
eines Threads aufrufen können, um auf das Ende des Threads zu warten. Wenn Sie nicht am Wert Ihres Threads interessiert sind, können Sie stattdessen auch die Methode
join
nutzen.
Diese Methode wartet, bis alle Threads außer dem Haupt-Thread und dem aktuellen Thread (bei denen es sich um deselben Thread handeln kann) beendet wurden:
# Warte darauf, dass alle Threads (außer dem aktuellen Thread und
# dem Haupt-Thread) beendet sind.
# Annahme: Während des Wartens werden keine neuen Threads gestartet.
def join_all
main = Thread.main # Haupt-Thread
current = Thread.current # Aktueller Thread
all = Thread.list # Alle laufenden Threads
# join für jeden Thread aufrufen
all.each {|t| t.join unless t == current or t == main }
end
9.9.1.2 Threads und unbehandelte Exceptions
Wenn eine Exception im Haupt-Thread geworfen und nicht abgefangen wird, gibt der Ruby-Interpreter eine Nachricht aus und beendet sich. In anderen Threads führen unbehandelte Exceptions dazu, dass der Thread stoppt. Standardmäßig wird dadurch vom Interpreter aber keine Nachricht ausgegeben, und er wird auch nicht beendet. Wenn ein Thread
t
aufgrund einer unbehandelten Exception endet und ein anderer Thread
s
entweder
t.join
oder
t.value
aufruft, wird die in
t
aufgetretene Exception im Thread
s
geworfen.
Wenn Sie möchten, dass jede unbehandelte Exception in jedem Thread zum Beenden des Interpreters führt, nutzen Sie die
Weitere Kostenlose Bücher