class Thread

Thread 是 Ruby 實作的並行程式設計模型。

需要多個執行緒的程式是 Ruby Thread 類別的理想候選者。

例如,我們可以使用 ::new 創建一個與主執行緒分離的新執行緒。

thr = Thread.new { puts "What's the big deal" }

然後,我們可以暫停主執行緒的執行,讓我們的新執行緒完成,使用 join

thr.join #=> "What's the big deal"

如果在主執行緒終止之前不呼叫 thr.join,則包括 thr 在內的所有其他執行緒都將被終止。

或者,您可以使用陣列一次處理多個執行緒,如下例所示

threads = []
threads << Thread.new { puts "What's the big deal" }
threads << Thread.new { 3.times { puts "Threads are fun!" } }

在創建幾個執行緒後,我們等待它們依次全部完成。

threads.each { |thr| thr.join }

要檢索執行緒的最後一個值,請使用 value

thr = Thread.new { sleep 1; "Useful value" }
thr.value #=> "Useful value"

Thread 初始化

為了創建新的執行緒,Ruby 提供了 ::new::start::fork。每個方法都必須提供一個區塊,否則會引發 ThreadError

當子類化 Thread 類時,你的子類的 initialize 方法將被 ::start::fork 忽略。否則,在你的 initialize 方法中確保呼叫 super。

Thread 終止

要終止執行緒,Ruby 提供了多種方法。

類方法 ::kill 用於退出給定的執行緒。

thr = Thread.new { sleep }
Thread.kill(thr) # sends exit() to thr

或者,你可以使用實例方法 exit,或其任何別名 killterminate

thr.exit

Thread 狀態

Ruby 提供了一些實例方法來查詢給定執行緒的狀態。使用 status 可以獲得當前執行緒的狀態字串。

thr = Thread.new { sleep }
thr.status # => "sleep"
thr.exit
thr.status # => false

你也可以使用 alive? 來判斷執行緒是否在運行或睡眠,以及 stop? 來判斷執行緒是否死亡或睡眠。

Thread 變數與作用域

由於執行緒是使用區塊創建的,對於變數作用域,其他 Ruby 區塊的相同規則也適用。在此區塊中創建的任何局部變數僅可供此執行緒訪問。

Fiber-local vs. Thread-local

每個纖程都有自己的用於 Thread#[] 存儲的桶。當你設置新的纖程本地變數時,它只能在此 Fiber 內訪問。舉例說明

Thread.new {
  Thread.current[:foo] = "bar"
  Fiber.new {
    p Thread.current[:foo] # => nil
  }.resume
}.join

此示例使用 [] 來獲取,[]= 來設置纖程本地變數,你也可以使用 keys 列出給定執行緒的纖程本地變數,以及使用 key? 檢查纖程本地變數是否存在。

至於線程本地變數,它們在整個線程的範圍內是可訪問的。根據以下示例

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

你可以看到線程本地變數 :foo 傳遞到纖程中,並且在線程結束時被更改為 2

此示例使用 thread_variable_set 創建新的線程本地變數,並使用 thread_variable_get 引用它們。

還有 thread_variables 以列出所有線程本地變數,以及 thread_variable?

Exception處理

當執行緒內部引發未處理的例外時,它將終止。預設情況下,此例外不會傳播到其他執行緒。例外會被儲存,當另一個執行緒呼叫valuejoin時,該例外將在該執行緒中重新引發。

t = Thread.new{ raise 'something went wrong' }
t.value #=> RuntimeError: something went wrong

可以使用Thread#raise實例方法從執行緒外部引發例外,該方法與Kernel#raise的參數相同。

設置Thread.abort_on_exception = true,Thread#abort_on_exception = true,或 $DEBUG = true將導致後續在執行緒中引發的未處理例外在主執行緒中自動重新引發。

通過新增的類方法::handle_interrupt,現在可以使用執行緒異步處理例外。

排程

Ruby 提供了一些支援在程式中排程執行緒的方法。

第一種方法是使用類方法::stop,將當前正在執行的執行緒休眠並安排另一個執行緒的執行。

一旦執行緒休眠,您可以使用實例方法wakeup將您的執行緒標記為可排程的。

您還可以嘗試::pass,該方法試圖將執行權轉交給另一個執行緒,但取決於操作系統,正在運行的執行緒是否會切換。相同情況適用於priority,該方法允許您向執行緒調度程序提示您希望在轉交執行權時優先考慮的執行緒。此方法也取決於操作系統,在某些平台上可能會被忽略。

公共類方法

abort_on_exception → true 或 false 點擊切換來源

返回全域“異常中止”條件的狀態。

默認值為false

當設置為true時,如果任何執行緒被例外中止,則引發的例外將在主執行緒中重新引發。

也可以由全域 $DEBUG 標誌或命令行選項-d指定。

另請參閱::abort_on_exception=

還有一個實例級別的方法可以為特定執行緒設置此屬性,請參閱abort_on_exception

static VALUE
rb_thread_s_abort_exc(VALUE _)
{
    return RBOOL(GET_THREAD()->vm->thread_abort_on_exception);
}
abort_on_exception= boolean → true 或 false 點擊切換來源

當設置為true時,如果任何執行緒被例外中止,則引發的例外將在主執行緒中重新引發。返回新狀態。

Thread.abort_on_exception = true
t1 = Thread.new do
  puts  "In new thread"
  raise "Exception from thread"
end
sleep(1)
puts "not reached"

這將產生

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

另請參閱::abort_on_exception

還有一個實例級別的方法可以為特定執行緒設置此屬性,請參閱abort_on_exception=

static VALUE
rb_thread_s_abort_exc_set(VALUE self, VALUE val)
{
    GET_THREAD()->vm->thread_abort_on_exception = RTEST(val);
    return val;
}
current → thread 點擊切換來源

返回當前正在執行的線程。

Thread.current   #=> #<Thread:0x401bdf4c run>
static VALUE
thread_s_current(VALUE klass)
{
    return rb_thread_current();
}
each_caller_location{ |loc| ... } → nil 點擊以切換源代碼

將當前執行堆疊的每個帧作為回溯位置對象逐個產生。

static VALUE
each_caller_location(VALUE unused)
{
    rb_ec_partial_backtrace_object(GET_EC(), 2, ALL_BACKTRACE_LINES, NULL, FALSE, TRUE);
    return Qnil;
}
exit → thread 點擊以切換源代碼

終止當前正在運行的線程並安排運行另一個線程。

如果此線程已經標記為將被終止,::exit 返回 Thread

如果這是主線程或最後一個線程,則退出進程。

static VALUE
rb_thread_exit(VALUE _)
{
    rb_thread_t *th = GET_THREAD();
    return rb_thread_kill(th->self);
}
start([args]*) {|args| block } → thread 點擊以切換源代碼
fork([args]*) {|args| block } → thread

基本上與 ::new 相同。但是,如果類別 Thread 是子類別化的,那麼在該子類別中調用 start 將不會調用子類別的 initialize 方法。

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), &params);
}
handle_interrupt(hash) { ... } → block 的結果 點擊以切換源代碼

更改異步中斷的定時。

中斷 意味著異步事件及相應的程序由 Thread#raiseThread#kill、信號捕獲(尚未支援)和主線程終止(如果主線程終止,則所有其他線程將被殺死)。

給定的 hash 具有像 ExceptionClass => :TimingSymbol 這樣的配對。其中 ExceptionClass 是由給定塊處理的中斷。TimingSymbol 可以是以下符號之一

:immediate

立即調用中斷。

:on_blocking

BlockingOperation 時調用中斷。

:never

從不調用所有中斷。

BlockingOperation 意味著該操作將阻塞調用線程,例如讀取和寫入。在 CRuby 實現中,BlockingOperation 是在沒有 GVL 的情況下執行的任何操作。

屏蔽的異步中斷將延遲到啟用它們。此方法類似於 sigprocmask(3)。

注意

異步中斷難以使用。

如果需要在線程之間通信,請考慮使用其他方法,如 Queue

或者深入理解此方法後再使用它們。

使用方法

在這個例子中,我們可以防止Thread#raise的異常發生。

使用:never TimingSymbol,RuntimeError異常將始終在主線程的第一個區塊中被忽略。在第二個::handle_interrupt區塊中,我們可以有目的地處理RuntimeError異常。

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"

在我們忽略RuntimeError異常時,安全地編寫資源分配代碼是安全的。然後,確保區塊是我們可以安全地釋放資源的地方。

防範Timeout::Error

在下一個例子中,我們將防範Timeout::Error異常。這將有助於在正常確保子句中發生Timeout::Error異常時防止資源洩漏。對於此示例,我們使用標準庫Timeout的幫助,來自lib/timeout.rb

require 'timeout'
Thread.handle_interrupt(Timeout::Error => :never) {
  timeout(10){
    # Timeout::Error doesn't occur here
    Thread.handle_interrupt(Timeout::Error => :on_blocking) {
      # possible to be killed by Timeout::Error
      # while blocking operation
    }
    # Timeout::Error doesn't occur here
  }
}

timeout區塊的第一部分中,我們可以依賴於Timeout::Error被忽略。然後在Timeout::Error => :on_blocking區塊中,任何將阻塞調用線程的操作都可能引發Timeout::Error異常。

堆棧控制設置

可以堆疊多個::handle_interrupt區塊,以一次控制多個ExceptionClass和TimingSymbol。

Thread.handle_interrupt(FooError => :never) {
  Thread.handle_interrupt(BarError => :never) {
     # FooError and BarError are prohibited.
  }
}

與ExceptionClass的繼承

所有從ExceptionClass參數繼承的異常都會被考慮。

Thread.handle_interrupt(Exception => :never) {
  # all exceptions inherited from Exception are prohibited.
}

為了處理所有中斷,請使用Object而不是Exception作為ExceptionClass,因為kill/terminate中斷不會被Exception處理。

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_RAW(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;
}
ignore_deadlock → true 或 false 點擊切換源

返回全局“忽略死鎖”條件的狀態。默認值為false,因此不會忽略死鎖條件。

另請參見::ignore_deadlock=

static VALUE
rb_thread_s_ignore_deadlock(VALUE _)
{
    return RBOOL(GET_THREAD()->vm->thread_ignore_deadlock);
}
ignore_deadlock = boolean → true 或 false 點擊切換源

返回新的狀態。當設置為true時,VM將不會檢查死鎖條件。僅當您的應用程序可以通過其他手段(例如信號)解除死鎖條件時,才有用。

Thread.ignore_deadlock = true
queue = Thread::Queue.new

trap(:SIGUSR1){queue.push "Received signal"}

# raises fatal error unless ignoring deadlock
puts queue.pop

另請參見::ignore_deadlock

static VALUE
rb_thread_s_ignore_deadlock_set(VALUE self, VALUE val)
{
    GET_THREAD()->vm->thread_ignore_deadlock = RTEST(val);
    return val;
}
kill(thread) → thread 點擊切換源

導致給定的thread退出,另請參見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
static VALUE
rb_thread_s_kill(VALUE obj, VALUE th)
{
    return rb_thread_kill(th);
}
list → array 點擊切換源

回傳所有可執行或停止的執行緒的 Thread 物件陣列。

Thread.new { sleep(200) }
Thread.new { 1000000.times {|i| i*i } }
Thread.new { Thread.stop }
Thread.list.each {|t| p t}

這將產生

#<Thread:0x401b3e84 sleep>
#<Thread:0x401b3f38 run>
#<Thread:0x401b3fb0 sleep>
#<Thread:0x401bdf4c run>
static VALUE
thread_list(VALUE _)
{
    return rb_thread_list();
}
main → thread 點擊切換來源

回傳主要執行緒。

static VALUE
rb_thread_s_main(VALUE klass)
{
    return rb_thread_main();
}
new { ... } → thread 點擊切換來源
new(*args, &proc) → thread
new(*args) { |args| ... } → thread

建立執行給定區塊的新執行緒。

傳遞給 ::new 的任何 args 將傳遞給該區塊。

arr = []
a, b, c = 1, 2, 3
Thread.new(a,b,c) { |d,e,f| arr << d << e << f }.join
arr #=> [1, 2, 3]

如果呼叫 ::new 而沒有區塊,將引發 ThreadError 例外。

如果你要派生自 Thread,請確保在你的 initialize 方法中呼叫 super,否則會引發 ThreadError

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;
}
pass → nil 點擊切換來源

給執行緒排程器一個提示,將執行傳遞給另一個執行緒。正在執行的執行緒可能會切換,也可能不會,這取決於作業系統和處理器。

static VALUE
thread_s_pass(VALUE klass)
{
    rb_thread_schedule();
    return Qnil;
}
pending_interrupt?(error = nil) → true/false 點擊切換來源

返回異步隊列是否為空。

由於 Thread::handle_interrupt 可用於推遲異步事件,因此可以使用此方法來確定是否存在任何推遲的事件。

如果發現此方法返回 true,則可以完成 :never 區塊。

例如,以下方法立即處理推遲的異步事件。

def Thread.kick_interrupt_immediately
  Thread.handle_interrupt(Object => :immediate) {
    Thread.pass
  }
end

如果給定 error,則僅檢查 error 類型的推遲事件。

用法

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

此範例也可以寫成以下形式,這是為了避免異步中斷。

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
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);
}
report_on_exception → true or false 點擊切換來源

返回全局“報告異常”條件的狀態。

默認值為自 Ruby 2.5 起的 true

當此標誌為 true 時創建的所有執行緒將在異常終止執行緒時在 $stderr 上報告一條消息。

Thread.new { 1.times { raise } }

將在 $stderr 上產生此輸出。

#<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'

這是為了早期捕捉線程中的錯誤。在某些情況下,您可能不希望得到這樣的輸出。有多種方法可以避免額外的輸出。

  • 如果異常不是故意的,最好的方法是修復異常的原因,以便它不再發生。

  • 如果異常是有意的,最好是在引發異常的地方進行救援,而不是讓它終止該 Thread

  • 如果可以保證 Thread 將與 Thread#joinThread#value 一起使用,則在啟動該 Thread 時,將其報告關閉為 Thread.current.report_on_exception = false 是安全的。然而,如果由於父線程被阻塞等原因而從未加入 Thread,則此處理可能要遲得多,或者根本不處理。

另請參閱 ::report_on_exception=

還有一個用於為特定線程設置此設置的實例級方法,請參閱 report_on_exception=

static VALUE
rb_thread_s_report_exc(VALUE _)
{
    return RBOOL(GET_THREAD()->vm->thread_report_on_exception);
}
report_on_exception= boolean → true 或 false 點擊切換源

返回新狀態。當設置為 true 時,後續創建的所有線程將繼承該條件並在發生異常時在 $stderr 上報告消息

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"

這將產生

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

另請參閱 ::report_on_exception

還有一個用於為特定線程設置此設置的實例級方法,請參閱 report_on_exception=

static VALUE
rb_thread_s_report_exc_set(VALUE self, VALUE val)
{
    GET_THREAD()->vm->thread_report_on_exception = RTEST(val);
    return val;
}
start([args]*) {|args| block } → thread 點擊以切換源代碼
fork([args]*) {|args| block } → thread

基本上與 ::new 相同。但是,如果類別 Thread 是子類別化的,那麼在該子類別中調用 start 將不會調用子類別的 initialize 方法。

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), &params);
}
stop → nil 點擊切換源

停止當前線程的執行,將其放入“睡眠”狀態,並安排執行另一個線程。

a = Thread.new { print "a"; Thread.stop; print "c" }
sleep 0.1 while a.status!='sleep'
print "b"
a.run
a.join
#=> "abc"
static VALUE
thread_stop(VALUE _)
{
    return rb_thread_stop();
}

公共實例方法

thr[sym] → obj 或 nil 點擊切換源

屬性參考—返回一個纖維本地變量的值(如果不明確在 Fiber 內部),使用符號或字符串名稱。如果指定的變量不存在,則返回 nil

[
  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

這將產生

#<Thread:0x00000002a54220 dead>: A
#<Thread:0x00000002a541a8 dead>: B
#<Thread:0x00000002a54130 dead>: C

Thread#[]Thread#[]= 不是線程本地的,而是纖維本地的。這種混淆在 Ruby 1.8 中不存在,因為自 Ruby 1.9 以來才有纖維。Ruby 1.9 選擇了方法的行為作為纖維本地以保存動態作用域的後綴成語。

def meth(newvalue)
  begin
    oldvalue = Thread.current[:name]
    Thread.current[:name] = newvalue
    yield
  ensure
    Thread.current[:name] = oldvalue
  end
end

如果方法是線程本地的,則這種成語可能無法作為動態作用域工作,如果給定的塊切換了纖維。

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.)

有關線程本地變量,請參閱 thread_variable_getthread_variable_set

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);
}
thr[sym] = obj → obj 點擊以切換源碼

屬性賦值—使用符號或字符串來設置或創建一個區域線程變量的值。

另見 Thread#[]

有關線程本地變量,請參見 thread_variable_setthread_variable_get

static VALUE
rb_thread_aset(VALUE self, VALUE id, VALUE val)
{
    return rb_thread_local_aset(self, rb_to_id(id), val);
}
abort_on_exception → true 或 false 點擊切換來源

返回此 thr 的線程本地“異常中止”條件的狀態。

默認值為false

另見 abort_on_exception=

還有一個用於設置所有線程的類級方法,請參見 ::abort_on_exception

static VALUE
rb_thread_abort_exc(VALUE thread)
{
    return RBOOL(rb_thread_ptr(thread)->abort_on_exception);
}
abort_on_exception= boolean → true 或 false 點擊切換來源

當設置為 true 時,如果此 thr 因異常而中止,則異常將在主線程中重新引發。

另見 abort_on_exception

還有一個用於設置所有線程的類級方法,請參見 ::abort_on_exception=

static VALUE
rb_thread_abort_exc_set(VALUE thread, VALUE val)
{
    rb_thread_ptr(thread)->abort_on_exception = RTEST(val);
    return val;
}
add_trace_func(proc) → proc 點擊以切換源碼

proc 添加為追蹤的處理程序。

參見 Thread#set_trace_funcKernel#set_trace_func

static VALUE
thread_add_trace_func_m(VALUE obj, VALUE trace)
{
    thread_add_trace_func(GET_EC(), rb_thread_ptr(obj), trace);
    return trace;
}
alive? → true or false 點擊以切換源碼

如果 thr 正在運行或睡眠,則返回 true

thr = Thread.new { }
thr.join                #=> #<Thread:0x401b3fb0 dead>
Thread.current.alive?   #=> true
thr.alive?              #=> false

另見 stop?status

static VALUE
rb_thread_alive_p(VALUE thread)
{
    return RBOOL(!thread_finished(rb_thread_ptr(thread)));
}
backtrace → array or nil 點擊以切換源碼

返回目標線程的當前回溯。

static VALUE
rb_thread_backtrace_m(int argc, VALUE *argv, VALUE thval)
{
    return rb_vm_thread_backtrace(argc, argv, thval);
}
backtrace_locations(*args) → array or nil 點擊以切換源碼

返回目標線程的執行堆棧—一個包含回溯位置對象的數組。

有關更多信息,請參見 Thread::Backtrace::Location

此方法的行為類似於 Kernel#caller_locations,但適用於特定線程。

static VALUE
rb_thread_backtrace_locations_m(int argc, VALUE *argv, VALUE thval)
{
    return rb_vm_thread_backtrace_locations(argc, argv, thval);
}
exit → thr

終止 thr 並安排運行另一個線程,返回已終止的 Thread。如果這是主線程或最後一個線程,則退出進程。

別名:kill
fetch(sym) → obj 點擊以切換來源
fetch(sym) { } → obj
fetch(sym, default) → obj

返回給定鍵的纖程本地變量。如果找不到鍵,有幾個選項:如果沒有其他參數,它將引發 KeyError 異常;如果給定了 default,那麼它將被返回;如果指定了可選代碼塊,則將運行該代碼塊並返回其結果。參見 Thread#[]Hash#fetch

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];
    }
}
group → thgrp 或 nil 點擊以切換來源

返回包含給定線程的 ThreadGroup

Thread.main.group   #=> #<ThreadGroup:0x4029d914>
VALUE
rb_thread_group(VALUE thread)
{
    return rb_thread_ptr(thread)->thgroup;
}
inspect
別名:to_s
join → thr 點擊以切換來源
join(limit) → thr

調用線程將暫停執行並運行此 thr

直到 thr 退出或直到給定的 limit 秒數過去為止,才返回。

如果時間限制過期,將返回 nil,否則將返回 thr

未加入的任何線程將在主程序退出時被終止。

如果 thr 先前引發了異常且未設置 ::abort_on_exception 或 $DEBUG 標誌(因此尚未處理異常),則此時將處理該異常。

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"

下面的示例說明了 limit 參數。

y = Thread.new { 4.times { sleep 0.1; puts 'tick... ' }}
puts "Waiting" until y.join(0.15)

這將產生

tick...
Waiting
tick...
Waiting
tick...
tick...
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);
}
key?(sym) → true 或 false 點擊以切換來源

如果給定的字符串(或符號)作為纖程本地變量存在,則返回 true

me = Thread.current
me[:oliver] = "a"
me.key?(:oliver)    #=> true
me.key?(:stanley)   #=> false
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));
}
keys → array 點擊以切換來源

返回纖程本地變量的名稱陣列(作為符號)。

thr = Thread.new do
  Thread.current[:cat] = 'meow'
  Thread.current["dog"] = 'woof'
end
thr.join   #=> #<Thread:0x401b3f10 dead>
thr.keys   #=> [:dog, :cat]
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;
}
kill → thr 點擊以切換來源

終止 thr 並安排運行另一個線程,返回已終止的 Thread。如果這是主線程或最後一個線程,則退出進程。

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;
}
也別名為:terminateexit
name → string 點擊以切換來源

顯示線程的名稱。

static VALUE
rb_thread_getname(VALUE thread)
{
    return rb_thread_ptr(thread)->name;
}
name=(name) → string 點擊以切換來源

將給定的名稱設置給 Ruby 線程。在某些平台上,它可能會將名稱設置為 pthread 和/或 kernel。

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;
}
native_thread_id → integer 點擊以切換來源

回傳 Ruby 執行緒使用的原生執行緒 ID。

該 ID 取決於作業系統。(不是由 pthread_self(3) 返回的 POSIX 執行緒 ID)

  • 在 Linux 上,它是由 gettid(2) 返回的 TID。

  • 在 macOS 上,它是由 pthread_threadid_np(3) 返回的系統全局唯一整數執行緒 ID。

  • 在 FreeBSD 上,它是由 pthread_getthreadid_np(3) 返回的執行緒的唯一整數 ID。

  • 在 Windows 上,它是由 GetThreadId() 返回的執行緒識別碼。

  • 在其他平台上,它會引發 NotImplementedError。

注意:如果執行緒尚未關聯或已解除關聯於原生執行緒,則返回 nil。如果 Ruby 實作使用 M:N 執行緒模型,則 ID 可能會根據時機而改變。

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);
}
pending_interrupt?(error = nil) → true/false 點擊切換來源

返回目標執行緒的非同步佇列是否為空。

如果給定 error,則僅檢查 error 類型的推遲事件。

更多信息請參見 Thread.html#method-c-pending_interrupt-3F。

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;
    }
}
優先順序 → 整數 點擊切換來源

返回 thr 的優先順序。默認情況下,繼承自創建新執行緒的當前執行緒;初始主執行緒的默認值為零;優先順序較高的執行緒比優先順序較低的執行緒運行頻率更高(但優先順序較低的執行緒也可以運行)。

這只是給 Ruby 執行緒排程器的提示。在某些平台上可能會被忽略。

Thread.current.priority   #=> 0
static VALUE
rb_thread_priority(VALUE thread)
{
    return INT2NUM(rb_thread_ptr(thread)->priority);
}
優先順序= 整數 → thr 點擊切換來源

thr 的優先順序設置為 整數。優先順序較高的執行緒比優先順序較低的執行緒運行頻率更高(但優先順序較低的執行緒也可以運行)。

這只是給 Ruby 執行緒排程器的提示。在某些平台上可能會被忽略。

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
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);
}
提高 點擊切換來源
提高(string)
提高(exception [, string [, array]])

從給定執行緒引發異常。調用者不必是 thr。更多信息請參見 Kernel.html#method-i-raise。

Thread.abort_on_exception = true
a = Thread.new { sleep(200) }
a.raise("Gotcha")

這將產生

prog.rb:3: Gotcha (RuntimeError)
 from prog.rb:2:in `initialize'
 from prog.rb:2:in `new'
 from prog.rb:2
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;
}
report_on_exception → true or false 點擊切換來源

返回此 thr 的執行緒本地“報告異常”條件的狀態。

在創建新執行緒(Thread.html)時的默認值是全局標誌 Thread.report_on_exception 的值。

另請參見 Thread.html#method-i-report_on_exception-3D。

還有一個用於設置所有新執行緒的類級方法,請參見 Thread.html#method-c-report_on_exception-3D。

static VALUE
rb_thread_report_exc(VALUE thread)
{
    return RBOOL(rb_thread_ptr(thread)->report_on_exception);
}
report_on_exception= boolean → true 或 false 點擊切換源

當設置為 true 時,如果異常導致此 thr 崩潰,將在 $stderr 上打印消息。有關詳細信息,請參見 Thread.html#method-c-report_on_exception。

另請參見 Thread.html#method-i-report_on_exception。

還有一個用於設置所有新執行緒的類級方法,請參見 Thread.html#method-c-report_on_exception-3D。

static VALUE
rb_thread_report_exc_set(VALUE thread, VALUE val)
{
    rb_thread_ptr(thread)->report_on_exception = RTEST(val);
    return val;
}
執行 → thr 點擊切換來源

喚醒 thr,使其有資格進行排程。

a = Thread.new { puts "a"; Thread.stop; puts "c" }
sleep 0.1 while a.status!='sleep'
puts "Got here"
a.run
a.join

這將產生

a
Got here
c

另請參見實例方法 Thread.html#method-i-wakeup。

VALUE
rb_thread_run(VALUE thread)
{
    rb_thread_wakeup(thread);
    rb_thread_schedule();
    return thread;
}
set_trace_func(proc) → proc 點擊切換源碼
set_trace_func(nil) → nil

thr上建立proc作為追蹤的處理程序,如果參數是nil,則禁用追蹤。

參見Kernel#set_trace_func

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;
    }
}
status → 字串、false 或 nil 點擊切換源碼

返回thr的狀態。

"sleep"

如果此線程正在睡眠或等待I/O時返回

"run"

當此線程正在執行時返回

"aborting"

如果此線程正在中止時返回

false

當此線程正常終止時返回

nil

如果以異常終止時返回。

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"

參見實例方法alive?stop?

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));
    }
}
stop? → true 或 false 點擊切換源碼

如果thr已經死亡或正在睡眠,則返回true

a = Thread.new { Thread.stop }
b = Thread.current
a.stop?   #=> true
b.stop?   #=> false

參見也alive?status

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);
}
terminate → thr

終止 thr 並安排運行另一個線程,返回已終止的 Thread。如果這是主線程或最後一個線程,則退出進程。

別名:kill
thread_variable?(key) → true 或 false 點擊切換源碼

如果給定的字符串(或符號)作為線程本地變量存在,則返回true

me = Thread.current
me.thread_variable_set(:oliver, "a")
me.thread_variable?(:oliver)    #=> true
me.thread_variable?(:stanley)   #=> false

請注意,這些不是纖維本地變量。請參見Thread#[]Thread#thread_variable_get獲取更多詳細信息。

static VALUE
rb_thread_variable_p(VALUE thread, VALUE key)
{
    VALUE locals;

    if (LIKELY(!THREAD_LOCAL_STORAGE_INITIALISED_P(thread))) {
        return Qfalse;
    }
    locals = rb_thread_local_storage(thread);

    return RBOOL(rb_hash_lookup(locals, rb_to_symbol(key)) != Qnil);
}
thread_variable_get(key) → obj 或 nil 點擊切換源碼

返回已設置的線程本地變量的值。請注意,這些與纖維本地值不同。對於纖維本地值,請參見Thread#[]Thread#[]=

Thread本地值隨著線程一起傳遞,並不考慮纖維。例如

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]

返回線程本地的值“bar”,而纖維本地則返回nil。纖維在同一線程中執行,因此線程本地值可用。

static VALUE
rb_thread_variable_get(VALUE thread, VALUE key)
{
    VALUE locals;

    if (LIKELY(!THREAD_LOCAL_STORAGE_INITIALISED_P(thread))) {
        return Qnil;
    }
    locals = rb_thread_local_storage(thread);
    return rb_hash_aref(locals, rb_to_symbol(key));
}
thread_variable_set(key, value) 點擊切換源代碼

將線程局部變量 key 設置為 value。請注意這些變量是針對線程的,而不是針對纖程的。更多信息請參閱 Thread#thread_variable_getThread#[]

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);
}
thread_variables → 陣列 點擊切換源代碼

返回線程局部變量的名稱(作為符號)的陣列。

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]

請注意,這些不是纖維本地變量。請參見Thread#[]Thread#thread_variable_get獲取更多詳細信息。

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;
}
to_s → 字串 點擊切換源代碼

thr 的名稱、id 和狀態轉儲到字串中。

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;
}
也別名為: inspect
value → 對象 點擊切換源代碼

使用 join 等待 thr 完成,並返回其值,或者引發終止線程的異常。

a = Thread.new { 2 + 2 }
a.value   #=> 4

b = Thread.new { raise 'something went wrong' }
b.value   #=> RuntimeError: something went wrong
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;
}
wakeup → thr 點擊切換源代碼

將給定的線程標記為可進行調度,但可能仍然在I/O上被阻塞。

注意: 這不會調用調度器,詳細信息請參閱 run

c = Thread.new { Thread.stop; puts "hey!" }
sleep 0.1 while c.status!='sleep'
c.wakeup
c.join
#=> "hey!"
VALUE
rb_thread_wakeup(VALUE thread)
{
    if (!RTEST(rb_thread_wakeup_alive(thread))) {
        rb_raise(rb_eThreadError, "killed thread");
    }
    return thread;
}