類別 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
,或其別名kill
或terminate
。
thr.exit
Thread
狀態¶ ↑
Ruby 提供了幾個用於查詢給定線程狀態的實例方法。要獲取當前線程狀態的字符串,請使用status
thr = Thread.new { sleep } thr.status # => "sleep" thr.exit thr.status # => false
您還可以使用alive?
來判斷線程是否正在運行或睡眠,以及stop?
來判斷線程是否已死亡或睡眠。
Thread
變量和作用域¶ ↑
由於線程是用塊創建的,因此其他 Ruby 塊的變量作用域規則也適用。在此塊中創建的任何本地變量僅可由此線程訪問。
纖維本地 vs. 線程本地¶ ↑
每個纖維都有自己的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
處理¶ ↑
當線程內發生未處理的異常時,它將終止。默認情況下,此異常不會傳播到其他線程。該異常被存儲,當另一個線程調用value
或join
時,該異常將在該線程中重新引發。
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
也是如此,它允許您提示線程調度程序優先處理哪些線程在傳遞執行權時。此方法還依賴於操作系統,並且在某些平台上可能會被忽略。
公共類方法
返回全局“异常时中止”条件的状态。
默认值为false
。
当设置为true
时,如果任何线程因异常而中止,则引发的异常将在主线程中重新引发。
也可以通过全局$DEBUG标志或命令行选项-d
来指定。
还有一个实例级别的方法可以为特定线程设置此选项,请参见abort_on_exception
。
static VALUE rb_thread_s_abort_exc(VALUE _) { return RBOOL(GET_THREAD()->vm->thread_abort_on_exception); }
当设置为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; }
返回当前正在执行的线程。
Thread.current #=> #<Thread:0x401bdf4c run>
static VALUE thread_s_current(VALUE klass) { return rb_thread_current(); }
将当前执行堆栈的每个帧作为回溯位置对象进行处理。
static VALUE each_caller_location(VALUE unused) { rb_ec_partial_backtrace_object(GET_EC(), 2, ALL_BACKTRACE_LINES, NULL, FALSE, TRUE); return Qnil; }
更改非同步中斷的時序。
中斷 意味著非同步事件及對應的程序,包括 Thread#raise
、Thread#kill
、信號捕獲(目前尚未支援)以及主執行緒終止(如果主執行緒終止,則所有其他執行緒將被終止)。
給定的 hash
包含像 ExceptionClass => :TimingSymbol
的配對。其中 ExceptionClass 是由給定區塊處理的中斷。TimingSymbol 可以是以下符號之一
:immediate
-
立即調用中斷。
:on_blocking
-
在 阻塞操作 時調用中斷。
:never
-
從不調用所有中斷。
阻塞操作 意味著該操作將阻塞呼叫執行緒,例如讀取和寫入。在 CRuby 實現中,阻塞操作 是在沒有 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
异常時,可以安全地编写我们的資源分配代碼。然後,ensure 區塊是我們可以安全地釋放資源的地方。
防範 Timeout::Error¶ ↑
在下一個示例中,我們將防範 Timeout::Error 异常。這將有助於防止在正常 ensure 子句期間發生 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; }
返回全局“忽略死鎖”條件的狀態。默認值為 false
,因此不會忽略死鎖條件。
另請參閱 ::ignore_deadlock=
。
static VALUE rb_thread_s_ignore_deadlock(VALUE _) { return RBOOL(GET_THREAD()->vm->thread_ignore_deadlock); }
返回新的狀態。當設置為 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; }
導致給定的 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); }
返回所有可運行或已停止的線程的 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(); }
返回主線程。
static VALUE rb_thread_s_main(VALUE klass) { return rb_thread_main(); }
創建執行給定塊的新線程。
任何給定給 ::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; }
給線程調度程序一個提示,以將執行傳遞給另一個線程。運行中的線程可能會切換,也可能不會,這取決於操作系統和處理器。
static VALUE thread_s_pass(VALUE klass) { rb_thread_schedule(); return Qnil; }
返回非同步佇列是否為空。
由於 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); }
返回全局“報告異常”條件的狀態。
自 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#join
或Thread#value
與Thread
加入,則在啟動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); }
返回新狀態。當設置為 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; }
停止當前執行緒的執行,將其置於“休眠”狀態,並安排另一個執行緒的執行。
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(); }
公共實例方法
屬性引用—返回一個纖程本地變量的值(如果未明確在 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_get
和 thread_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); }
屬性分配—設置或創建一個纖程本地變量的值,可以使用符號或字符串。
另請參閱 Thread#[]
。
對於線程本地變量,請參閱 thread_variable_set
和 thread_variable_get
。
static VALUE rb_thread_aset(VALUE self, VALUE id, VALUE val) { return rb_thread_local_aset(self, rb_to_id(id), val); }
返回此 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); }
當設置為 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; }
將proc添加為追蹤程序的處理程序。
請參閱Thread#set_trace_func
和Kernel#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; }
返回目標線程的當前回溯。
static VALUE rb_thread_backtrace_m(int argc, VALUE *argv, VALUE thval) { return rb_vm_thread_backtrace(argc, argv, thval); }
返回目標線程的執行堆棧,包含回溯位置對象的陣列。
有關詳細信息,請參閱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); }
返回給定鍵的纖維本地變量。如果找不到鍵,則有幾個選項:如果沒有其他參數,則將引發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]; } }
返回包含給定線程的ThreadGroup
。
Thread.main.group #=> #<ThreadGroup:0x4029d914>
VALUE rb_thread_group(VALUE thread) { return rb_thread_ptr(thread)->thgroup; }
調用線程將暫停執行並運行此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); }
如果給定的字符串(或符號)作為一個纖程本地變量存在,則返回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)); }
返回一個包含纖程本地變量名稱(作為Symbols)的數組。
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; }
終止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; }
顯示線程的名稱。
static VALUE rb_thread_getname(VALUE thread) { return rb_thread_ptr(thread)->name; }
將給定的名稱設置給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; }
返回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); }
返回目標執行緒的異步佇列是否為空。
如果給定了 error
,則僅檢查類型為 error
的延遲事件。
有關更多信息,請參見::pending_interrupt?
。
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的優先順序設置為整數。優先順序較高的執行緒將比優先順序較低的執行緒更頻繁地運行(但優先順序較低的執行緒也可以運行)。
這只是對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); }
從給定的執行緒中引發異常。調用者不需要是thr
。有關更多信息,請參見Kernel#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; }
返回此thr
的線程本地“在異常上報告”的狀態。
創建Thread
時的默認值是全局標誌Thread.report_on_exception
的值。
另請參見report_on_exception=
。
還有一個類級方法可以為所有新執行緒設置此值,請參見::report_on_exception=
。
static VALUE rb_thread_report_exc(VALUE thread) { return RBOOL(rb_thread_ptr(thread)->report_on_exception); }
當設置為true
時,如果異常使此thr
終止,則會在$stderr上打印一條消息。有關詳細信息,請參見::report_on_exception
。
另請參見report_on_exception
。
還有一個類級方法可以為所有新執行緒設置此值,請參見::report_on_exception=
。
static VALUE rb_thread_report_exc_set(VALUE thread, VALUE val) { rb_thread_ptr(thread)->report_on_exception = RTEST(val); return val; }
喚醒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
另請參見實例方法wakeup
。
VALUE rb_thread_run(VALUE thread) { rb_thread_wakeup(thread); rb_thread_schedule(); return thread; }
在thr上設置proc作為追蹤的處理程序,或者如果參數為nil
則禁用追蹤。
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; } }
返回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"
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)); } }
如果 thr
已結束或正在睡眠,則返回 true
a = Thread.new { Thread.stop } b = Thread.current a.stop? #=> true b.stop? #=> false
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); }
如果給定的字符串(或符號)存在作為執行緒本地變量,則返回 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#[]
和 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)); }
將具有 key
的執行緒本地設置為 value
。請注意這些僅適用於執行緒,而不是纖維。有關詳細信息,請參閱 Thread#thread_variable_get
和 Thread#[]
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); }
回傳一個字串陣列,包含了執行緒本地變數的名稱(以符號表示)。
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; }
將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; }
使用 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; }
將指定的執行緒標記為可進行排程,但仍可能因 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; }