Klasse Thread
Threads sind die Ruby-Implementierung für ein paralleles Programmiermodell.
Programme, die mehrere Ausführungs-Threads erfordern, sind ein perfekter Kandidat für Rubys Thread-Klasse.
Zum Beispiel können wir einen neuen Thread erstellen, der von der Ausführung des Haupt-Threads getrennt ist, indem wir ::new verwenden.
thr = Thread.new { puts "What's the big deal" }
Dann können wir die Ausführung des Haupt-Threads anhalten und unseren neuen Thread beenden lassen, indem wir join verwenden.
thr.join #=> "What's the big deal"
Wenn wir thr.join nicht aufrufen, bevor der Haupt-Thread beendet wird, werden alle anderen Threads, einschließlich thr, beendet.
Alternativ können Sie ein Array zur gleichzeitigen Verarbeitung mehrerer Threads verwenden, wie im folgenden Beispiel:
threads = [] threads << Thread.new { puts "What's the big deal" } threads << Thread.new { 3.times { puts "Threads are fun!" } }
Nachdem einige Threads erstellt wurden, warten wir darauf, dass sie alle nacheinander beendet werden.
threads.each { |thr| thr.join }
Um den letzten Wert eines Threads abzurufen, verwenden Sie value.
thr = Thread.new { sleep 1; "Useful value" } thr.value #=> "Useful value"
Thread-Initialisierung
Um neue Threads zu erstellen, bietet Ruby ::new, ::start und ::fork. Mit jeder dieser Methoden muss ein Block bereitgestellt werden, andernfalls wird ein ThreadError ausgelöst.
Beim Unterklassen der Thread-Klasse wird die initialize-Methode Ihrer Unterklasse von ::start und ::fork ignoriert. Andernfalls stellen Sie sicher, dass Sie super in Ihrer initialize-Methode aufrufen.
Thread-Beendigung
Für die Beendigung von Threads bietet Ruby eine Vielzahl von Möglichkeiten.
Die Klassenmethode ::kill dient dazu, einen gegebenen Thread zu beenden.
thr = Thread.new { sleep } Thread.kill(thr) # sends exit() to thr
Alternativ können Sie die Instanzmethode exit oder eine ihrer Aliasnamen kill oder terminate verwenden.
thr.exit
Thread-Status
Ruby bietet einige Instanzmethoden zur Abfrage des Zustands eines gegebenen Threads. Um einen String mit dem aktuellen Zustand des Threads zu erhalten, verwenden Sie status.
thr = Thread.new { sleep } thr.status # => "sleep" thr.exit thr.status # => false
Sie können auch alive? verwenden, um festzustellen, ob der Thread läuft oder schläft, und stop?, ob der Thread tot oder schläft.
Thread-Variablen und Gültigkeitsbereich
Da Threads mit Blöcken erstellt werden, gelten für die Gültigkeitsbereiche von Variablen die gleichen Regeln wie für andere Ruby-Blöcke. Alle lokalen Variablen, die innerhalb dieses Blocks erstellt werden, sind nur für diesen Thread zugänglich.
Fiber-lokal vs. Thread-lokal
Jede Fiber hat ihren eigenen Speicherbereich für Thread#[]-Speicherung. Wenn Sie eine neue Fiber-lokale Variable setzen, ist sie nur innerhalb dieser Fiber zugänglich. Zur Veranschaulichung:
Thread.new { Thread.current[:foo] = "bar" Fiber.new { p Thread.current[:foo] # => nil }.resume }.join
Dieses Beispiel verwendet [] zum Abrufen und []= zum Setzen von Fiber-lokalen Variablen. Sie können auch keys verwenden, um die Fiber-lokalen Variablen eines bestimmten Threads aufzulisten, und key?, um zu prüfen, ob eine Fiber-lokale Variable existiert.
Was Thread-lokale Variablen betrifft, so sind sie im gesamten Gültigkeitsbereich des Threads zugänglich. Angenommen, das folgende Beispiel:
Thread.new{ Thread.current.thread_variable_set(:foo, 1) p Thread.current.thread_variable_get(:foo) # => 1 Fiber.new{ Thread.current.thread_variable_set(:foo, 2) p Thread.current.thread_variable_get(:foo) # => 2 }.resume p Thread.current.thread_variable_get(:foo) # => 2 }.join
Sie sehen, dass die Thread-lokale Variable :foo in die Fiber übernommen wurde und am Ende des Threads auf 2 geändert wurde.
Dieses Beispiel verwendet thread_variable_set zum Erstellen neuer Thread-lokaler Variablen und thread_variable_get zum Referenzieren dieser.
Es gibt auch thread_variables zum Auflisten aller Thread-lokalen Variablen und thread_variable? zum Prüfen, ob eine gegebene Thread-lokale Variable existiert.
Exception-Behandlung
Wenn eine unbehandelte Ausnahme innerhalb eines Threads ausgelöst wird, beendet sich dieser. Standardmäßig wird diese Ausnahme nicht an andere Threads weitergegeben. Die Ausnahme wird gespeichert und wenn ein anderer Thread value oder join aufruft, wird die Ausnahme in diesem Thread erneut ausgelöst.
t = Thread.new{ raise 'something went wrong' } t.value #=> RuntimeError: something went wrong
Eine Ausnahme kann von außerhalb des Threads mit der Instanzmethode Thread#raise ausgelöst werden, die dieselben Parameter wie Kernel#raise annimmt.
Das Setzen von Thread.abort_on_exception = true, Thread#abort_on_exception = true oder $DEBUG = true bewirkt, dass eine nachfolgende unbehandelte Ausnahme, die in einem Thread ausgelöst wird, automatisch im Haupt-Thread erneut ausgelöst wird.
Mit der Klassenmethode ::handle_interrupt können Sie jetzt Ausnahmen asynchron mit Threads behandeln.
Zeitplanung
Ruby bietet einige Möglichkeiten zur Unterstützung der Zeitplanung von Threads in Ihrem Programm.
Die erste Methode ist die Verwendung der Klassenmethode ::stop, um den aktuell laufenden Thread anzuhalten und die Ausführung eines anderen Threads zu planen.
Sobald ein Thread angehalten ist, können Sie die Instanzmethode wakeup verwenden, um Ihren Thread für die Zeitplanung zu kennzeichnen.
Sie können auch ::pass versuchen, der versucht, die Ausführung an einen anderen Thread zu übergeben, aber davon abhängig ist, ob das Betriebssystem einen laufenden Thread wechselt oder nicht. Dasselbe gilt für priority, mit dem Sie dem Thread-Scheduler Hinweise geben können, welche Threads bei der Übergabe der Ausführung Vorrang haben sollen. Diese Methode ist ebenfalls vom Betriebssystem abhängig und kann auf einigen Plattformen ignoriert werden.
Öffentliche Klassenmethoden
Source
static VALUE
rb_thread_s_abort_exc(VALUE _)
{
return RBOOL(GET_THREAD()->vm->thread_abort_on_exception);
}
Gibt den Status der globalen Bedingung „abort on exception“ zurück.
Standardmäßig ist dies false.
Wenn auf true gesetzt, wird, wenn ein Thread durch eine Ausnahme abgebrochen wird, die ausgelöste Ausnahme im Haupt-Thread erneut ausgelöst.
Kann auch durch das globale $DEBUG-Flag oder die Befehlszeilenoption -d angegeben werden.
Siehe auch ::abort_on_exception=.
Es gibt auch eine Instanzmethode, um dies für einen bestimmten Thread festzulegen. Siehe abort_on_exception.
Source
static VALUE
rb_thread_s_abort_exc_set(VALUE self, VALUE val)
{
GET_THREAD()->vm->thread_abort_on_exception = RTEST(val);
return val;
}
Wenn auf true gesetzt, wird, wenn ein Thread durch eine Ausnahme abgebrochen wird, die ausgelöste Ausnahme im Haupt-Thread erneut ausgelöst. Gibt den neuen Zustand zurück.
Thread.abort_on_exception = true t1 = Thread.new do puts "In new thread" raise "Exception from thread" end sleep(1) puts "not reached"
Dies erzeugt
In new thread prog.rb:4: Exception from thread (RuntimeError) from prog.rb:2:in `initialize' from prog.rb:2:in `new' from prog.rb:2
Siehe auch ::abort_on_exception.
Es gibt auch eine Instanzmethode, um dies für einen bestimmten Thread festzulegen. Siehe abort_on_exception=.
Source
static VALUE
thread_s_current(VALUE klass)
{
return rb_thread_current();
}
Gibt den aktuell ausgeführten Thread zurück.
Thread.current #=> #<Thread:0x401bdf4c run>
Source
static VALUE
each_caller_location(int argc, VALUE *argv, VALUE _)
{
rb_execution_context_t *ec = GET_EC();
long n, lev = ec_backtrace_range(ec, argc, argv, 1, 1, &n);
if (lev >= 0 && n != 0) {
rb_ec_partial_backtrace_object(ec, lev, n, NULL, FALSE, TRUE);
}
return Qnil;
}
Liefert jeden Frame des aktuellen Ausführungsstapels als Backtrace-Speicherortobjekt.
Source
static VALUE
rb_thread_exit(VALUE _)
{
rb_thread_t *th = GET_THREAD();
return rb_thread_kill(th->self);
}
Beendet den aktuell laufenden Thread und plant die Ausführung eines anderen Threads. Wenn dies der Haupt-Thread oder der letzte Thread ist, wird der Prozess beendet.
Wenn dieser Thread bereits zum Beenden markiert ist, gibt ::exit den Thread zurück.
Wenn dies der Haupt-Thread oder der letzte Thread ist, wird der Prozess beendet.
Source
static VALUE
thread_start(VALUE klass, VALUE args)
{
struct thread_create_params params = {
.type = thread_invoke_type_proc,
.args = args,
.proc = rb_block_proc(),
};
return thread_create_core(rb_thread_alloc(klass), ¶ms);
}
Source
static VALUE
rb_thread_s_handle_interrupt(VALUE self, VALUE mask_arg)
{
VALUE mask = Qundef;
rb_execution_context_t * volatile ec = GET_EC();
rb_thread_t * volatile th = rb_ec_thread_ptr(ec);
volatile VALUE r = Qnil;
enum ruby_tag_type state;
if (!rb_block_given_p()) {
rb_raise(rb_eArgError, "block is needed.");
}
mask_arg = rb_to_hash_type(mask_arg);
if (OBJ_FROZEN(mask_arg) && rb_hash_compare_by_id_p(mask_arg)) {
mask = Qnil;
}
rb_hash_foreach(mask_arg, handle_interrupt_arg_check_i, (VALUE)&mask);
if (UNDEF_P(mask)) {
return rb_yield(Qnil);
}
if (!RTEST(mask)) {
mask = mask_arg;
}
else if (RB_TYPE_P(mask, T_HASH)) {
OBJ_FREEZE(mask);
}
rb_ary_push(th->pending_interrupt_mask_stack, mask);
if (!rb_threadptr_pending_interrupt_empty_p(th)) {
th->pending_interrupt_queue_checked = 0;
RUBY_VM_SET_INTERRUPT(th->ec);
}
EC_PUSH_TAG(th->ec);
if ((state = EC_EXEC_TAG()) == TAG_NONE) {
r = rb_yield(Qnil);
}
EC_POP_TAG();
rb_ary_pop(th->pending_interrupt_mask_stack);
if (!rb_threadptr_pending_interrupt_empty_p(th)) {
th->pending_interrupt_queue_checked = 0;
RUBY_VM_SET_INTERRUPT(th->ec);
}
RUBY_VM_CHECK_INTS(th->ec);
if (state) {
EC_JUMP_TAG(th->ec, state);
}
return r;
}
Ändert die asynchronen Interrupt-Zeitpläne.
Interrupt bedeutet asynchrones Ereignis und entsprechende Prozedur durch Thread#raise, Thread#kill, Signal-Trap (noch nicht unterstützt) und Beendigung des Haupt-Threads (wenn der Haupt-Thread beendet wird, werden alle anderen Threads beendet).
Der angegebene hash hat Paare wie ExceptionClass => :TimingSymbol. Wobei die ExceptionClass der durch den Block behandelte Interrupt ist. Das TimingSymbol kann eines der folgenden Symbole sein:
:immediate-
Interrupts sofort aufrufen.
:on_blocking-
Interrupts während BlockingOperation aufrufen.
:never-
Nie alle Interrupts aufrufen.
BlockingOperation bedeutet, dass die Operation den aufrufenden Thread blockiert, wie z.B. Lesen und Schreiben. Bei der CRuby-Implementierung ist BlockingOperation jede Operation, die ohne GVL ausgeführt wird.
Maskierte asynchrone Interrupts werden verzögert, bis sie aktiviert sind. Diese Methode ähnelt sigprocmask(3).
HINWEIS
Asynchrone Interrupts sind schwierig zu verwenden.
Wenn Sie zwischen Threads kommunizieren müssen, ziehen Sie bitte eine andere Methode wie Queue in Betracht.
Oder verwenden Sie sie mit tiefem Verständnis dieser Methode.
Verwendung
In diesem Beispiel können wir uns vor Thread#raise-Ausnahmen schützen.
Unter Verwendung des :never TimingSymbol wird die RuntimeError-Ausnahme im ersten Block des Haupt-Threads immer ignoriert. Im zweiten ::handle_interrupt-Block können wir RuntimeError-Ausnahmen gezielt behandeln.
th = Thread.new do Thread.handle_interrupt(RuntimeError => :never) { begin # You can write resource allocation code safely. Thread.handle_interrupt(RuntimeError => :immediate) { # ... } ensure # You can write resource deallocation code safely. end } end Thread.pass # ... th.raise "stop"
Während wir die RuntimeError-Ausnahme ignorieren, ist es sicher, unseren Ressourcenallokationscode zu schreiben. Dann ist der ensure-Block, in dem wir unsere Ressourcen sicher freigeben können.
Stack-Steuerungseinstellungen
Es ist möglich, mehrere Ebenen von ::handle_interrupt-Blöcken zu stapeln, um mehr als eine ExceptionClass und ein TimingSymbol gleichzeitig zu steuern.
Thread.handle_interrupt(FooError => :never) { Thread.handle_interrupt(BarError => :never) { # FooError and BarError are prohibited. } }
Vererbung mit ExceptionClass
Alle von der ExceptionClass-Parameter abgeleiteten Ausnahmen werden berücksichtigt.
Thread.handle_interrupt(Exception => :never) { # all exceptions inherited from Exception are prohibited. }
Zur Behandlung aller Interrupts verwenden Sie Object und nicht Exception als ExceptionClass, da Kill/Terminate-Interrupts nicht von Exception behandelt werden.
Source
static VALUE
rb_thread_s_ignore_deadlock(VALUE _)
{
return RBOOL(GET_THREAD()->vm->thread_ignore_deadlock);
}
Gibt den Status der globalen Bedingung „ignore deadlock“ zurück. Standardmäßig ist dies false, sodass Deadlock-Bedingungen nicht ignoriert werden.
Siehe auch ::ignore_deadlock=.
Source
static VALUE
rb_thread_s_ignore_deadlock_set(VALUE self, VALUE val)
{
GET_THREAD()->vm->thread_ignore_deadlock = RTEST(val);
return val;
}
Gibt den neuen Zustand zurück. Wenn auf true gesetzt, prüft die VM nicht auf Deadlock-Bedingungen. Es ist nur sinnvoll, dies zu setzen, wenn Ihre Anwendung eine Deadlock-Bedingung auf andere Weise brechen kann, z. B. durch ein Signal.
Thread.ignore_deadlock = true queue = Thread::Queue.new trap(:SIGUSR1){queue.push "Received signal"} # raises fatal error unless ignoring deadlock puts queue.pop
Siehe auch ::ignore_deadlock.
Source
static VALUE
rb_thread_s_kill(VALUE obj, VALUE th)
{
return rb_thread_kill(th);
}
Bewirkt, dass der gegebene thread beendet wird, siehe auch Thread::exit.
count = 0 a = Thread.new { loop { count += 1 } } sleep(0.1) #=> 0 Thread.kill(a) #=> #<Thread:0x401b3d30 dead> count #=> 93947 a.alive? #=> false
Source
static VALUE
thread_list(VALUE _)
{
return rb_thread_list();
}
Gibt ein Array von Thread-Objekten für alle Threads zurück, die entweder ausführbar oder angehalten sind.
Thread.new { sleep(200) } Thread.new { 1000000.times {|i| i*i } } Thread.new { Thread.stop } Thread.list.each {|t| p t}
Dies erzeugt
#<Thread:0x401b3e84 sleep> #<Thread:0x401b3f38 run> #<Thread:0x401b3fb0 sleep> #<Thread:0x401bdf4c run>
Source
static VALUE
rb_thread_s_main(VALUE klass)
{
return rb_thread_main();
}
Gibt den Haupt-Thread zurück.
Source
static VALUE
thread_s_new(int argc, VALUE *argv, VALUE klass)
{
rb_thread_t *th;
VALUE thread = rb_thread_alloc(klass);
if (GET_RACTOR()->threads.main->status == THREAD_KILLED) {
rb_raise(rb_eThreadError, "can't alloc thread");
}
rb_obj_call_init_kw(thread, argc, argv, RB_PASS_CALLED_KEYWORDS);
th = rb_thread_ptr(thread);
if (!threadptr_initialized(th)) {
rb_raise(rb_eThreadError, "uninitialized thread - check '%"PRIsVALUE"#initialize'",
klass);
}
return thread;
}
Erstellt einen neuen Thread, der den gegebenen Block ausführt.
Alle args, die an ::new übergeben werden, werden an den Block übergeben.
arr = [] a, b, c = 1, 2, 3 Thread.new(a,b,c) { |d,e,f| arr << d << e << f }.join arr #=> [1, 2, 3]
Eine ThreadError-Ausnahme wird ausgelöst, wenn ::new ohne Block aufgerufen wird.
Wenn Sie Thread unterklassifizieren möchten, stellen Sie sicher, dass Sie super in Ihrer initialize-Methode aufrufen, andernfalls wird eine ThreadError ausgelöst.
Source
static VALUE
thread_s_pass(VALUE klass)
{
rb_thread_schedule();
return Qnil;
}
Gibt dem Thread-Scheduler einen Hinweis, die Ausführung an einen anderen Thread zu übergeben. Ein laufender Thread kann möglicherweise wechseln oder auch nicht, dies hängt vom Betriebssystem und Prozessor ab.
Source
static VALUE
rb_thread_s_pending_interrupt_p(int argc, VALUE *argv, VALUE self)
{
return rb_thread_pending_interrupt_p(argc, argv, GET_THREAD()->self);
}
Gibt zurück, ob die asynchrone Warteschlange leer ist oder nicht.
Da Thread::handle_interrupt verwendet werden kann, um asynchrone Ereignisse zu verzögern, kann diese Methode verwendet werden, um festzustellen, ob verzögerte Ereignisse vorhanden sind.
Wenn diese Methode true zurückgibt, können Sie :never-Blöcke abschließen.
Zum Beispiel verarbeitet die folgende Methode verzögerte asynchrone Ereignisse sofort.
def Thread.kick_interrupt_immediately Thread.handle_interrupt(Object => :immediate) { Thread.pass } end
Wenn error angegeben wird, prüfen Sie nur auf verzögerte Ereignisse vom Typ error.
Verwendung
th = Thread.new{
Thread.handle_interrupt(RuntimeError => :on_blocking){
while true
...
# reach safe point to invoke interrupt
if Thread.pending_interrupt?
Thread.handle_interrupt(Object => :immediate){}
end
...
end
}
}
...
th.raise # stop thread
Dieses Beispiel kann auch wie folgt geschrieben werden, was Sie verwenden sollten, um asynchrone Interrupts zu vermeiden.
flag = true
th = Thread.new{
Thread.handle_interrupt(RuntimeError => :on_blocking){
while true
...
# reach safe point to invoke interrupt
break if flag == false
...
end
}
}
...
flag = false # stop thread
Source
static VALUE
rb_thread_s_report_exc(VALUE _)
{
return RBOOL(GET_THREAD()->vm->thread_report_on_exception);
}
Gibt den Status der globalen Bedingung „report on exception“ zurück.
Standardmäßig ist dies seit Ruby 2.5 true.
Alle Threads, die erstellt werden, wenn diese Flagge auf true gesetzt ist, melden eine Nachricht auf $stderr, wenn eine Ausnahme den Thread beendet.
Thread.new { 1.times { raise } }
wird diese Ausgabe auf $stderr erzeugen
#<Thread:...> terminated with exception (report_on_exception is true):
Traceback (most recent call last):
2: from -e:1:in `block in <main>'
1: from -e:1:in `times'
Dies geschieht, um Fehler in Threads frühzeitig zu erkennen. In einigen Fällen möchten Sie diese Ausgabe möglicherweise nicht. Es gibt mehrere Möglichkeiten, die zusätzliche Ausgabe zu vermeiden:
-
Wenn die Ausnahme nicht beabsichtigt ist, ist es am besten, die Ursache der Ausnahme zu beheben, damit sie nicht mehr auftritt.
-
Wenn die Ausnahme beabsichtigt ist, ist es vielleicht besser, sie näher an der Stelle abzufangen, an der sie ausgelöst wird, anstatt sie den
Threadbeenden zu lassen. -
Wenn garantiert ist, dass der
ThreadmitThread#joinoderThread#valueverbunden wird, ist es sicher, diesen Bericht mitThread.current.report_on_exception = falsezu deaktivieren, wenn derThreadgestartet wird. Dies kann jedoch die Ausnahme viel später behandeln oder gar nicht, wenn derThreadnie verbunden wird, weil der Eltern-Thread blockiert ist usw.
Siehe auch ::report_on_exception=.
Es gibt auch eine Instanzmethode, um dies für einen bestimmten Thread festzulegen. Siehe report_on_exception=.
Source
static VALUE
rb_thread_s_report_exc_set(VALUE self, VALUE val)
{
GET_THREAD()->vm->thread_report_on_exception = RTEST(val);
return val;
}
Gibt den neuen Zustand zurück. Wenn auf true gesetzt, erben alle danach erstellten Threads die Bedingung und melden eine Nachricht auf $stderr, wenn eine Ausnahme einen Thread beendet.
Thread.report_on_exception = true t1 = Thread.new do puts "In new thread" raise "Exception from thread" end sleep(1) puts "In the main thread"
Dies erzeugt
In new thread #<Thread:...prog.rb:2> terminated with exception (report_on_exception is true): Traceback (most recent call last): prog.rb:4:in `block in <main>': Exception from thread (RuntimeError) In the main thread
Siehe auch ::report_on_exception.
Es gibt auch eine Instanzmethode, um dies für einen bestimmten Thread festzulegen. Siehe report_on_exception=.
Source
static VALUE
thread_start(VALUE klass, VALUE args)
{
struct thread_create_params params = {
.type = thread_invoke_type_proc,
.args = args,
.proc = rb_block_proc(),
};
return thread_create_core(rb_thread_alloc(klass), ¶ms);
}
Source
static VALUE
thread_stop(VALUE _)
{
return rb_thread_stop();
}
Stoppt die Ausführung des aktuellen Threads, versetzt ihn in einen „Schlaf“-Zustand und plant die Ausführung eines anderen Threads.
a = Thread.new { print "a"; Thread.stop; print "c" } sleep 0.1 while a.status!='sleep' print "b" a.run a.join #=> "abc"
Öffentliche Instanzmethoden
Source
static VALUE
rb_thread_aref(VALUE thread, VALUE key)
{
ID id = rb_check_id(&key);
if (!id) return Qnil;
return rb_thread_local_aref(thread, id);
}
Attributreferenz — Gibt den Wert einer Fiber-lokalen Variable zurück (die Wurzel-Fiber des aktuellen Threads, wenn nicht explizit innerhalb einer Fiber), unter Verwendung eines Symbols oder eines String-Namens. Wenn die angegebene Variable nicht existiert, wird nil zurückgegeben.
[ Thread.new { Thread.current["name"] = "A" }, Thread.new { Thread.current[:name] = "B" }, Thread.new { Thread.current["name"] = "C" } ].each do |th| th.join puts "#{th.inspect}: #{th[:name]}" end
Dies erzeugt
#<Thread:0x00000002a54220 dead>: A #<Thread:0x00000002a541a8 dead>: B #<Thread:0x00000002a54130 dead>: C
Thread#[] und Thread#[]= sind nicht Thread-lokal, sondern Fiber-lokal. Diese Verwirrung existierte in Ruby 1.8 nicht, da Fibers erst seit Ruby 1.9 verfügbar sind. Ruby 1.9 wählt, dass sich die Methoden Fiber-lokal verhalten, um das folgende Idiom für dynamische Gültigkeitsbereiche zu retten.
def meth(newvalue) begin oldvalue = Thread.current[:name] Thread.current[:name] = newvalue yield ensure Thread.current[:name] = oldvalue end end
Das Idiom funktioniert möglicherweise nicht als dynamische Gültigkeitsbereich, wenn sich die Methoden Thread-lokal verhalten und ein gegebener Block die Fiber wechselt.
f = Fiber.new { meth(1) { Fiber.yield } } meth(2) { f.resume } f.resume p Thread.current[:name] #=> nil if fiber-local #=> 2 if thread-local (The value 2 is leaked to outside of meth method.)
Für Thread-lokale Variablen siehe bitte thread_variable_get und thread_variable_set.
Source
static VALUE
rb_thread_aset(VALUE self, VALUE id, VALUE val)
{
return rb_thread_local_aset(self, rb_to_id(id), val);
}
Attributzuweisung — Setzt oder erstellt den Wert einer Fiber-lokalen Variable unter Verwendung eines Symbols oder eines Strings.
Siehe auch Thread#[].
Für Thread-lokale Variablen siehe bitte thread_variable_set und thread_variable_get.
Source
static VALUE
rb_thread_abort_exc(VALUE thread)
{
return RBOOL(rb_thread_ptr(thread)->abort_on_exception);
}
Gibt den Status der Thread-lokalen Bedingung „abort on exception“ für diesen thr zurück.
Standardmäßig ist dies false.
Siehe auch abort_on_exception=.
Es gibt auch eine Klassenmethode, um dies für alle Threads festzulegen. Siehe ::abort_on_exception.
Source
static VALUE
rb_thread_abort_exc_set(VALUE thread, VALUE val)
{
rb_thread_ptr(thread)->abort_on_exception = RTEST(val);
return val;
}
Wenn auf true gesetzt, wird, wenn dieser thr durch eine Ausnahme abgebrochen wird, die ausgelöste Ausnahme im Haupt-Thread erneut ausgelöst.
Siehe auch abort_on_exception.
Es gibt auch eine Klassenmethode, um dies für alle Threads festzulegen. Siehe ::abort_on_exception=.
Source
static VALUE
thread_add_trace_func_m(VALUE obj, VALUE trace)
{
thread_add_trace_func(GET_EC(), rb_thread_ptr(obj), trace);
return trace;
}
Fügt proc als Handler für das Tracing hinzu.
Siehe Thread#set_trace_func und Kernel#set_trace_func.
Source
static VALUE
rb_thread_alive_p(VALUE thread)
{
return RBOOL(!thread_finished(rb_thread_ptr(thread)));
}
Source
static VALUE
rb_thread_backtrace_m(int argc, VALUE *argv, VALUE thval)
{
return rb_vm_thread_backtrace(argc, argv, thval);
}
Gibt den aktuellen Backtrace des Ziel-Threads zurück.
Source
static VALUE
rb_thread_backtrace_locations_m(int argc, VALUE *argv, VALUE thval)
{
return rb_vm_thread_backtrace_locations(argc, argv, thval);
}
Gibt den Ausführungsstapel für den Ziel-Thread zurück – ein Array, das Backtrace-Speicherortobjekte enthält.
Siehe Thread::Backtrace::Location für weitere Informationen.
Diese Methode verhält sich ähnlich wie Kernel#caller_locations, außer dass sie für einen bestimmten Thread gilt.
Beendet thr und plant die Ausführung eines anderen Threads, wobei der beendete Thread zurückgegeben wird. Wenn dies der Haupt-Thread oder der letzte Thread ist, wird der Prozess beendet. Beachten Sie, dass der Aufrufer nicht auf die Beendigung des Threads wartet, wenn der Empfänger sich vom aktuell laufenden Thread unterscheidet. Die Beendigung ist asynchron, und der Thread kann noch eine kleine Menge Ruby-Code ausführen, bevor er beendet wird.
Source
static VALUE
rb_thread_fetch(int argc, VALUE *argv, VALUE self)
{
VALUE key, val;
ID id;
rb_thread_t *target_th = rb_thread_ptr(self);
int block_given;
rb_check_arity(argc, 1, 2);
key = argv[0];
block_given = rb_block_given_p();
if (block_given && argc == 2) {
rb_warn("block supersedes default value argument");
}
id = rb_check_id(&key);
if (id == recursive_key) {
return target_th->ec->local_storage_recursive_hash;
}
else if (id && target_th->ec->local_storage &&
rb_id_table_lookup(target_th->ec->local_storage, id, &val)) {
return val;
}
else if (block_given) {
return rb_yield(key);
}
else if (argc == 1) {
rb_key_err_raise(rb_sprintf("key not found: %+"PRIsVALUE, key), self, key);
}
else {
return argv[1];
}
}
Gibt eine Fiber-lokale Variable für den angegebenen Schlüssel zurück. Wenn der Schlüssel nicht gefunden werden kann, gibt es mehrere Optionen: Ohne weitere Argumente wird eine KeyError-Ausnahme ausgelöst; wenn default angegeben ist, wird dieser zurückgegeben; wenn der optionale Codeblock angegeben ist, wird dieser ausgeführt und sein Ergebnis zurückgegeben. Siehe Thread#[] und Hash#fetch.
Source
VALUE
rb_thread_group(VALUE thread)
{
return rb_thread_ptr(thread)->thgroup;
}
Gibt die ThreadGroup zurück, die den gegebenen Thread enthält.
Thread.main.group #=> #<ThreadGroup:0x4029d914>
Source
static VALUE
thread_join_m(int argc, VALUE *argv, VALUE self)
{
VALUE timeout = Qnil;
rb_hrtime_t rel = 0, *limit = 0;
if (rb_check_arity(argc, 0, 1)) {
timeout = argv[0];
}
// Convert the timeout eagerly, so it's always converted and deterministic
/*
* This supports INFINITY and negative values, so we can't use
* rb_time_interval right now...
*/
if (NIL_P(timeout)) {
/* unlimited */
}
else if (FIXNUM_P(timeout)) {
rel = rb_sec2hrtime(NUM2TIMET(timeout));
limit = &rel;
}
else {
limit = double2hrtime(&rel, rb_num2dbl(timeout));
}
return thread_join(rb_thread_ptr(self), timeout, limit);
}
Der aufrufende Thread wird die Ausführung unterbrechen und diesen thr ausführen.
Kehrt erst zurück, wenn thr beendet ist oder wenn die gegebene limit Sekunde vergangen sind.
Wenn das Zeitlimit abläuft, wird nil zurückgegeben, andernfalls thr.
Threads, die nicht verbunden werden, werden beendet, wenn das Hauptprogramm beendet wird.
Wenn thr zuvor eine Ausnahme ausgelöst hat und die Flags ::abort_on_exception oder $DEBUG nicht gesetzt sind (sodass die Ausnahme noch nicht verarbeitet wurde), wird sie zu diesem Zeitpunkt verarbeitet.
a = Thread.new { print "a"; sleep(10); print "b"; print "c" } x = Thread.new { print "x"; Thread.pass; print "y"; print "z" } x.join # Let thread x finish, thread a will be killed on exit. #=> "axyz"
Das folgende Beispiel veranschaulicht den limit-Parameter.
y = Thread.new { 4.times { sleep 0.1; puts 'tick... ' }} puts "Waiting" until y.join(0.15)
Dies erzeugt
tick... Waiting tick... Waiting tick... tick...
Source
static VALUE
rb_thread_key_p(VALUE self, VALUE key)
{
VALUE val;
ID id = rb_check_id(&key);
struct rb_id_table *local_storage = rb_thread_ptr(self)->ec->local_storage;
if (!id || local_storage == NULL) {
return Qfalse;
}
return RBOOL(rb_id_table_lookup(local_storage, id, &val));
}
Gibt true zurück, wenn der gegebene String (oder Symbol) als Fiber-lokale Variable existiert.
me = Thread.current me[:oliver] = "a" me.key?(:oliver) #=> true me.key?(:stanley) #=> false
Source
static VALUE
rb_thread_keys(VALUE self)
{
struct rb_id_table *local_storage = rb_thread_ptr(self)->ec->local_storage;
VALUE ary = rb_ary_new();
if (local_storage) {
rb_id_table_foreach(local_storage, thread_keys_i, (void *)ary);
}
return ary;
}
Gibt ein Array der Namen der Fiber-lokalen Variablen (als Symbole) zurück.
thr = Thread.new do Thread.current[:cat] = 'meow' Thread.current["dog"] = 'woof' end thr.join #=> #<Thread:0x401b3f10 dead> thr.keys #=> [:dog, :cat]
Source
VALUE
rb_thread_kill(VALUE thread)
{
rb_thread_t *target_th = rb_thread_ptr(thread);
if (target_th->to_kill || target_th->status == THREAD_KILLED) {
return thread;
}
if (target_th == target_th->vm->ractor.main_thread) {
rb_exit(EXIT_SUCCESS);
}
RUBY_DEBUG_LOG("target_th:%u", rb_th_serial(target_th));
if (target_th == GET_THREAD()) {
/* kill myself immediately */
rb_threadptr_to_kill(target_th);
}
else {
threadptr_check_pending_interrupt_queue(target_th);
rb_threadptr_pending_interrupt_enque(target_th, RUBY_FATAL_THREAD_KILLED);
rb_threadptr_interrupt(target_th);
}
return thread;
}
Beendet thr und plant die Ausführung eines anderen Threads, wobei der beendete Thread zurückgegeben wird. Wenn dies der Haupt-Thread oder der letzte Thread ist, wird der Prozess beendet. Beachten Sie, dass der Aufrufer nicht auf die Beendigung des Threads wartet, wenn der Empfänger sich vom aktuell laufenden Thread unterscheidet. Die Beendigung ist asynchron, und der Thread kann noch eine kleine Menge Ruby-Code ausführen, bevor er beendet wird.
Source
static VALUE
rb_thread_getname(VALUE thread)
{
return rb_thread_ptr(thread)->name;
}
zeigt den Namen des Threads an.
Source
static VALUE
rb_thread_setname(VALUE thread, VALUE name)
{
rb_thread_t *target_th = rb_thread_ptr(thread);
if (!NIL_P(name)) {
rb_encoding *enc;
StringValueCStr(name);
enc = rb_enc_get(name);
if (!rb_enc_asciicompat(enc)) {
rb_raise(rb_eArgError, "ASCII incompatible encoding (%s)",
rb_enc_name(enc));
}
name = rb_str_new_frozen(name);
}
target_th->name = name;
if (threadptr_initialized(target_th) && target_th->has_dedicated_nt) {
native_set_another_thread_name(target_th->nt->thread_id, name);
}
return name;
}
Weist dem Ruby-Thread den gegebenen Namen zu. Auf einigen Plattformen kann dies den Namen für pthread und/oder den Kernel setzen.
Source
static VALUE
rb_thread_native_thread_id(VALUE thread)
{
rb_thread_t *target_th = rb_thread_ptr(thread);
if (rb_threadptr_dead(target_th)) return Qnil;
return native_thread_native_thread_id(target_th);
}
Gibt die native Thread-ID zurück, die vom Ruby-Thread verwendet wird.
Die ID ist vom Betriebssystem abhängig. (Nicht die POSIX-Thread-ID, die von pthread_self(3) zurückgegeben wird)
-
Unter Linux ist dies die TID, die von gettid(2) zurückgegeben wird.
-
Unter macOS ist dies die systemweit eindeutige integrale ID des Threads, die von pthread_threadid_np(3) zurückgegeben wird.
-
Unter FreeBSD ist dies die eindeutige integrale ID des Threads, die von pthread_getthreadid_np(3) zurückgegeben wird.
-
Unter Windows ist dies die Thread-Kennung, die von GetThreadId() zurückgegeben wird.
-
Auf anderen Plattformen wird eine
NotImplementedErrorausgelöst.
HINWEIS: Wenn der Thread noch nicht mit einem nativen Thread verbunden ist oder bereits davon getrennt wurde, wird nil zurückgegeben. Wenn die Ruby-Implementierung ein M:N-Thread-Modell verwendet, kann sich die ID je nach Zeitpunkt ändern.
Source
static VALUE
rb_thread_pending_interrupt_p(int argc, VALUE *argv, VALUE target_thread)
{
rb_thread_t *target_th = rb_thread_ptr(target_thread);
if (!target_th->pending_interrupt_queue) {
return Qfalse;
}
if (rb_threadptr_pending_interrupt_empty_p(target_th)) {
return Qfalse;
}
if (rb_check_arity(argc, 0, 1)) {
VALUE err = argv[0];
if (!rb_obj_is_kind_of(err, rb_cModule)) {
rb_raise(rb_eTypeError, "class or module required for rescue clause");
}
return RBOOL(rb_threadptr_pending_interrupt_include_p(target_th, err));
}
else {
return Qtrue;
}
}
Gibt zurück, ob die asynchrone Warteschlange für den Ziel-Thread leer ist oder nicht.
Wenn error angegeben wird, prüfen Sie nur auf verzögerte Ereignisse vom Typ error.
Siehe ::pending_interrupt? für weitere Informationen.
Source
static VALUE
rb_thread_priority(VALUE thread)
{
return INT2NUM(rb_thread_ptr(thread)->priority);
}
Gibt die Priorität von thr zurück. Standardmäßig wird sie vom aktuellen Thread geerbt, der den neuen Thread erstellt, oder ist Null für den anfänglichen Haupt-Thread; ein Thread mit höherer Priorität läuft häufiger als Threads mit niedrigerer Priorität (aber Threads mit niedrigerer Priorität können auch laufen).
Dies ist nur ein Hinweis für den Ruby-Thread-Scheduler. Es kann auf einigen Plattformen ignoriert werden.
Thread.current.priority #=> 0
Source
static VALUE
rb_thread_priority_set(VALUE thread, VALUE prio)
{
rb_thread_t *target_th = rb_thread_ptr(thread);
int priority;
#if USE_NATIVE_THREAD_PRIORITY
target_th->priority = NUM2INT(prio);
native_thread_apply_priority(th);
#else
priority = NUM2INT(prio);
if (priority > RUBY_THREAD_PRIORITY_MAX) {
priority = RUBY_THREAD_PRIORITY_MAX;
}
else if (priority < RUBY_THREAD_PRIORITY_MIN) {
priority = RUBY_THREAD_PRIORITY_MIN;
}
target_th->priority = (int8_t)priority;
#endif
return INT2NUM(target_th->priority);
}
Setzt die Priorität von thr auf integer. Threads mit höherer Priorität laufen häufiger als Threads mit niedrigerer Priorität (aber Threads mit niedrigerer Priorität können auch laufen).
Dies ist nur ein Hinweis für den Ruby-Thread-Scheduler. Es kann auf einigen Plattformen ignoriert werden.
count1 = count2 = 0 a = Thread.new do loop { count1 += 1 } end a.priority = -1 b = Thread.new do loop { count2 += 1 } end b.priority = -2 sleep 1 #=> 1 count1 #=> 622504 count2 #=> 5832
Source
static VALUE
thread_raise_m(int argc, VALUE *argv, VALUE self)
{
rb_thread_t *target_th = rb_thread_ptr(self);
const rb_thread_t *current_th = GET_THREAD();
threadptr_check_pending_interrupt_queue(target_th);
rb_threadptr_raise(target_th, argc, argv);
/* To perform Thread.current.raise as Kernel.raise */
if (current_th == target_th) {
RUBY_VM_CHECK_INTS(target_th->ec);
}
return Qnil;
}
Löst eine Ausnahme aus dem gegebenen Thread aus. Der Aufrufer muss nicht thr sein. Siehe Kernel#raise für weitere Informationen zu Argumenten.
Thread.abort_on_exception = true a = Thread.new { sleep(200) } a.raise("Gotcha")
Dies erzeugt
prog.rb:3: Gotcha (RuntimeError) from prog.rb:2:in `initialize' from prog.rb:2:in `new' from prog.rb:2
Source
static VALUE
rb_thread_report_exc(VALUE thread)
{
return RBOOL(rb_thread_ptr(thread)->report_on_exception);
}
Gibt den Status der Thread-lokalen Bedingung „report on exception“ für diesen thr zurück.
Der Standardwert beim Erstellen eines Thread ist der Wert des globalen Flags Thread.report_on_exception.
Siehe auch report_on_exception=.
Es gibt auch eine Klassenmethode, um dies für alle neuen Threads festzulegen. Siehe ::report_on_exception=.
Source
static VALUE
rb_thread_report_exc_set(VALUE thread, VALUE val)
{
rb_thread_ptr(thread)->report_on_exception = RTEST(val);
return val;
}
Wenn auf true gesetzt, wird eine Meldung auf $stderr ausgegeben, wenn eine Ausnahme diesen thr beendet. Siehe ::report_on_exception für Details.
Siehe auch report_on_exception.
Es gibt auch eine Klassenmethode, um dies für alle neuen Threads festzulegen. Siehe ::report_on_exception=.
Source
VALUE
rb_thread_run(VALUE thread)
{
rb_thread_wakeup(thread);
rb_thread_schedule();
return thread;
}
Weckt thr auf und macht ihn für die Zeitplanung verfügbar.
a = Thread.new { puts "a"; Thread.stop; puts "c" } sleep 0.1 while a.status!='sleep' puts "Got here" a.run a.join
Dies erzeugt
a Got here c
Siehe auch die Instanzmethode wakeup.
Source
static VALUE
thread_set_trace_func_m(VALUE target_thread, VALUE trace)
{
rb_execution_context_t *ec = GET_EC();
rb_thread_t *target_th = rb_thread_ptr(target_thread);
rb_threadptr_remove_event_hook(ec, target_th, call_trace_func, Qundef);
if (NIL_P(trace)) {
return Qnil;
}
else {
thread_add_trace_func(ec, target_th, trace);
return trace;
}
}
Legt proc auf thr als Handler für das Tracing fest oder deaktiviert das Tracing, wenn der Parameter nil ist.
Siehe Kernel#set_trace_func.
Source
static VALUE
rb_thread_status(VALUE thread)
{
rb_thread_t *target_th = rb_thread_ptr(thread);
if (rb_threadptr_dead(target_th)) {
if (!NIL_P(target_th->ec->errinfo) &&
!FIXNUM_P(target_th->ec->errinfo)) {
return Qnil;
}
else {
return Qfalse;
}
}
else {
return rb_str_new2(thread_status_name(target_th, FALSE));
}
}
Gibt den Status von thr zurück.
"sleep"-
Zurückgegeben, wenn dieser Thread schläft oder auf I/O wartet.
"run"-
Wenn dieser Thread ausgeführt wird.
"aborting"-
Wenn dieser Thread abbricht.
falsch-
Wenn dieser Thread normal beendet wurde.
nil-
Wenn mit einer Ausnahme beendet.
a = Thread.new { raise("die now") } b = Thread.new { Thread.stop } c = Thread.new { Thread.exit } d = Thread.new { sleep } d.kill #=> #<Thread:0x401b3678 aborting> a.status #=> nil b.status #=> "sleep" c.status #=> false d.status #=> "aborting" Thread.current.status #=> "run"
Source
static VALUE
rb_thread_stop_p(VALUE thread)
{
rb_thread_t *th = rb_thread_ptr(thread);
if (rb_threadptr_dead(th)) {
return Qtrue;
}
return RBOOL(th->status == THREAD_STOPPED || th->status == THREAD_STOPPED_FOREVER);
}
Beendet thr und plant die Ausführung eines anderen Threads, wobei der beendete Thread zurückgegeben wird. Wenn dies der Haupt-Thread oder der letzte Thread ist, wird der Prozess beendet. Beachten Sie, dass der Aufrufer nicht auf die Beendigung des Threads wartet, wenn der Empfänger sich vom aktuell laufenden Thread unterscheidet. Die Beendigung ist asynchron, und der Thread kann noch eine kleine Menge Ruby-Code ausführen, bevor er beendet wird.
Source
static VALUE
rb_thread_variable_p(VALUE thread, VALUE key)
{
VALUE locals;
VALUE symbol = rb_to_symbol(key);
if (LIKELY(!THREAD_LOCAL_STORAGE_INITIALISED_P(thread))) {
return Qfalse;
}
locals = rb_thread_local_storage(thread);
return RBOOL(rb_hash_lookup(locals, symbol) != Qnil);
}
Gibt true zurück, wenn der gegebene String (oder Symbol) als Thread-lokale Variable existiert.
me = Thread.current me.thread_variable_set(:oliver, "a") me.thread_variable?(:oliver) #=> true me.thread_variable?(:stanley) #=> false
Beachten Sie, dass dies keine Fiber-lokalen Variablen sind. Bitte siehe Thread#[] und Thread#thread_variable_get für weitere Details.
Source
static VALUE
rb_thread_variable_get(VALUE thread, VALUE key)
{
VALUE locals;
VALUE symbol = rb_to_symbol(key);
if (LIKELY(!THREAD_LOCAL_STORAGE_INITIALISED_P(thread))) {
return Qnil;
}
locals = rb_thread_local_storage(thread);
return rb_hash_aref(locals, symbol);
}
Gibt den Wert einer gesetzten Thread-lokalen Variable zurück. Beachten Sie, dass diese sich von Fiber-lokalen Werten unterscheiden. Für Fiber-lokale Werte siehe bitte Thread#[] und Thread#[]=.
Thread-lokale Werte werden mit Threads weitergegeben und respektieren keine Fibers. Zum Beispiel:
Thread.new { Thread.current.thread_variable_set("foo", "bar") # set a thread local Thread.current["foo"] = "bar" # set a fiber local Fiber.new { Fiber.yield [ Thread.current.thread_variable_get("foo"), # get the thread local Thread.current["foo"], # get the fiber local ] }.resume }.join.value # => ['bar', nil]
Der Wert „bar“ wird für die Thread-lokale Variable zurückgegeben, während nil für die Fiber-lokale Variable zurückgegeben wird. Die Fiber wird im selben Thread ausgeführt, sodass die Thread-lokalen Werte verfügbar sind.
Source
static VALUE
rb_thread_variable_set(VALUE thread, VALUE key, VALUE val)
{
VALUE locals;
if (OBJ_FROZEN(thread)) {
rb_frozen_error_raise(thread, "can't modify frozen thread locals");
}
locals = rb_thread_local_storage(thread);
return rb_hash_aset(locals, rb_to_symbol(key), val);
}
Setzt eine Thread-lokale Variable mit key auf value. Beachten Sie, dass diese lokal für Threads und nicht für Fibers sind. Bitte siehe Thread#thread_variable_get und Thread#[] für weitere Informationen.
Source
static VALUE
rb_thread_variables(VALUE thread)
{
VALUE locals;
VALUE ary;
ary = rb_ary_new();
if (LIKELY(!THREAD_LOCAL_STORAGE_INITIALISED_P(thread))) {
return ary;
}
locals = rb_thread_local_storage(thread);
rb_hash_foreach(locals, keys_i, ary);
return ary;
}
Gibt ein Array der Namen der Thread-lokalen Variablen (als Symbole) zurück.
thr = Thread.new do Thread.current.thread_variable_set(:cat, 'meow') Thread.current.thread_variable_set("dog", 'woof') end thr.join #=> #<Thread:0x401b3f10 dead> thr.thread_variables #=> [:dog, :cat]
Beachten Sie, dass dies keine Fiber-lokalen Variablen sind. Bitte siehe Thread#[] und Thread#thread_variable_get für weitere Details.
Source
static VALUE
rb_thread_to_s(VALUE thread)
{
VALUE cname = rb_class_path(rb_obj_class(thread));
rb_thread_t *target_th = rb_thread_ptr(thread);
const char *status;
VALUE str, loc;
status = thread_status_name(target_th, TRUE);
str = rb_sprintf("#<%"PRIsVALUE":%p", cname, (void *)thread);
if (!NIL_P(target_th->name)) {
rb_str_catf(str, "@%"PRIsVALUE, target_th->name);
}
if ((loc = threadptr_invoke_proc_location(target_th)) != Qnil) {
rb_str_catf(str, " %"PRIsVALUE":%"PRIsVALUE,
RARRAY_AREF(loc, 0), RARRAY_AREF(loc, 1));
}
rb_str_catf(str, " %s>", status);
return str;
}
Gibt den Namen, die ID und den Status von thr als String aus.
Source
static VALUE
thread_value(VALUE self)
{
rb_thread_t *th = rb_thread_ptr(self);
thread_join(th, Qnil, 0);
if (UNDEF_P(th->value)) {
// If the thread is dead because we forked th->value is still Qundef.
return Qnil;
}
return th->value;
}
Wartet, bis thr beendet ist, verwendet join, und gibt seinen Wert zurück oder löst die Ausnahme aus, die den Thread beendet hat.
a = Thread.new { 2 + 2 } a.value #=> 4 b = Thread.new { raise 'something went wrong' } b.value #=> RuntimeError: something went wrong
Source
VALUE
rb_thread_wakeup(VALUE thread)
{
if (!RTEST(rb_thread_wakeup_alive(thread))) {
rb_raise(rb_eThreadError, "killed thread");
}
return thread;
}
Markiert einen gegebenen Thread als zur Zeitplanung berechtigt, jedoch kann er weiterhin auf I/O blockiert sein.
Hinweis: Dies ruft nicht den Scheduler auf. Siehe run für weitere Informationen.
c = Thread.new { Thread.stop; puts "hey!" } sleep 0.1 while c.status!='sleep' c.wakeup c.join #=> "hey!"