Thread
Thread (스레드)
스레드는 Ruby의 동시성(concurrent) 프로그래밍 모델의 구현이에요. 여러 실행 스레드가 필요한 프로그램은 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"
출처: Ruby 4.0 API
본문
스레드 초기화
새 스레드를 만들기 위해 Ruby는 ::new, ::start, ::fork를 제공해요. 각 메서드에 반드시 블록을 주어야 하며, 주지 않으면 ThreadError가 발생해요.
Thread 클래스를 서브클래싱할 때, 서브클래스의 initialize 메서드는 ::start와 ::fork에서 무시돼요. 그 외에는 initialize에서 반드시 super를 호출하세요.
스레드 종료
스레드를 종료하는 방법은 여러 가지가 있어요. 클래스 메서드 ::kill은 주어진 스레드를 종료해요.
thr = Thread.new { sleep }
Thread.kill(thr) # exit()을 thr에 보내요
또는 인스턴스 메서드 exit(별칭 kill, terminate)을 사용할 수 있어요.
thr.exit
스레드 상태
status로 현재 스레드 상태를 나타내는 문자열을 얻을 수 있어요.
thr = Thread.new { sleep }
thr.status # => "sleep"
thr.exit
thr.status # => false
alive?로 스레드가 실행 중이거나 잠들어 있는지, stop?으로 죽었거나 잠들어 있는지를 알 수 있어요.
스레드 변수와 스코프
스레드는 블록으로 만들기 때문에 일반 Ruby 블록과 같은 변수 스코프 규칙이 적용돼요. 블록 안에서 만든 지역 변수는 그 스레드에서만 접근 가능해요.
Fiber-local vs Thread-local
각 fiber는 Thread#[] 저장소를 위한 **자체 버킷(bucket)**을 가져요. 새 fiber-local을 설정하면 그 Fiber 안에서만 접근할 수 있어요.
Thread.new {
Thread.current[:foo] = "bar"
Fiber.new {
p Thread.current[:foo] # => nil
}.resume
}.join
이 예시는 fiber-local을 가져오기 위해 []을, 설정하기 위해 []=을 썼어요. keys로 스레드의 fiber-local을 나열하고, key?로 fiber-local 존재 여부를 확인할 수 있어요.
스레드-local(thread-local)은 스레드 전체 스코프에서 접근 가능해요.
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
thread-local인 :foo가 fiber 안으로 이어졌다가(fiber-local과 달리), 스레드 끝에 가서는 2로 바뀐 걸 볼 수 있어요. 이 예시는 새 thread-local을 만들기 위해 thread_variable_set을, 참조하기 위해 thread_variable_get을 썼어요. 모든 thread-local을 나열하는 thread_variables와, thread-local 존재를 확인하는 thread_variable?도 있어요.
예외 처리
스레드 안에서 처리되지 않은 예외가 발생하면 그 스레드는 종료돼요. 기본적으로 이 예외는 다른 스레드로 전파되지 않아요. 예외는 저장됐다가, 다른 스레드가 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를 쓸 수도 있는데, 실행을 다른 스레드에 넘기려 시도하지만 실제로 실행 중인 스레드가 바뀔지는 OS에 달려 있어요. priority도 마찬가지인데, 실행을 넘길 때 어느 스레드를 우선할지 스레드 스케줄러에 힌트를 주는 것이에요. 이 메서드도 OS 의존적이며 일부 플랫폼에서는 무시될 수 있어요.
클래스 메서드 (Public Class Methods)
abort_on_exception → true or false
전역 "abort on exception" 조건의 상태를 돌려줘요. 기본값은 false예요. true로 설정하면 어떤 스레드가 예외로 중단되면 그 예외가 메인 스레드에서 다시 던져져요. 전역 $DEBUG 플래그나 명령줄 옵션 -d로도 지정할 수 있어요. ::abort_on_exception=도 참고하세요. 특정 스레드에만 설정하는 인스턴스 레벨 메서드도 있어요(abort_on_exception 참고).
abort_on_exception= boolean → true or 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
current → thread
현재 실행 중인 스레드를 돌려줘요.
Thread.current #=> #<Thread:0x401bdf4c run>
each_caller_location(...) { |loc| ... } → nil
현재 실행 스택의 각 프레임을 backtrace location 객체로 yield해요.
exit → thread
현재 실행 중인 스레드를 종료하고 다른 스레드 실행을 예약해요. 이미 kill 표시된 스레드면 ::exit는 그 Thread를 돌려줘요. 이게 메인 스레드나 마지막 스레드면 프로세스를 종료해요.
start([args]*) {|args| block } → thread / fork
기본적으로 ::new와 같아요. 다만 Thread를 서브클래싱한 경우 서브클래스에서 start를 호출해도 그 서브클래스의 initialize 메서드를 호출하지 않아요.
handle_interrupt(hash) { ... } → result of the block
비동기 인터럽트 타이밍을 변경해요. interrupt란 Thread#raise, Thread#kill, 시그널 trap(아직 미지원), 메인 스레드 종료(메인 스레드가 종료하면 다른 모든 스레드가 kill됨)로 인한 비동기 이벤트와 그에 대응하는 처리를 뜻해요.
주어진 hash는 ExceptionClass => :TimingSymbol 쌍을 가져요. 여기서 ExceptionClass는 해당 블록이 처리하는 인터럽트이고, TimingSymbol은 다음 중 하나예요.
:immediate- 인터럽트를 즉시 호출.:on_blocking- BlockingOperation 중에 인터럽트를 호출.:never- 모든 인터럽트를 절대 호출하지 않음.
BlockingOperation은 read/write처럼 호출 스레드를 블록하는 연산을 뜻해요. CRuby 구현에서는 GVL 없이 실행되는 어떤 연산이든 BlockingOperation이에요.
마스킹된 비동기 인터럽트는 활성화될 때까지 지연돼요. 이 메서드는 sigprocmask(3)와 비슷해요.
NOTE: 비동기 인터럽트는 쓰기 어려워요. 스레드 사이 통신이 필요하다면 Queue 같은 다른 방법을 고려하세요. 아니면 이 메서드를 아주 깊이 이해한 뒤에 쓰세요.
Usage 예시: 이 예시에서 Thread#raise 예외로부터 가드를 만들 수 있어요. :never TimingSymbol을 쓰면 RuntimeError 예외가 메인 스레드의 첫 블록에서 항상 무시돼요. 두 번째 ::handle_interrupt 블록에서는 RuntimeError 예외를 의도적으로 처리할 수 있어요.
th = Thread.new do
Thread.handle_interrupt(RuntimeError => :never) {
begin
# 리소스 할당 코드를 안전하게 쓸 수 있어요.
Thread.handle_interrupt(RuntimeError => :immediate) {
# ...
}
ensure
# 리소스 해제 코드를 안전하게 쓸 수 있어요.
end
}
end
Thread.pass
# ...
th.raise "stop"
RuntimeError 예외를 무시하는 동안에는 리소스 할당 코드를 안전하게 쓸 수 있어요. 그리고 ensure 블록이 리소스를 안전하게 해제하는 곳이에요.
Stack control settings: 여러 단계의 ::handle_interrupt 블록을 쌓아서 한 번에 둘 이상의 ExceptionClass와 TimingSymbol을 제어할 수 있어요.
Thread.handle_interrupt(FooError => :never) {
Thread.handle_interrupt(BarError => :never) {
# FooError와 BarError가 금지돼요.
}
}
Inheritance with ExceptionClass: ExceptionClass 파라미터로부터 상속된 모든 예외가 고려돼요.
Thread.handle_interrupt(Exception => :never) {
# Exception으로부터 상속된 모든 예외가 금지돼요.
}
모든 인터럽트를 처리하려면 ExceptionClass로 Exception이 아니라 Object를 사용하세요. kill/terminate 인터럽트는 Exception으로 처리되지 않기 때문이에요.
ignore_deadlock → true or false
전역 "ignore deadlock" 조건의 상태를 돌려줘요. 기본값은 false라 데드락 조건을 무시하지 않아요. ::ignore_deadlock=도 참고하세요.
ignore_deadlock= boolean → true or false
새 상태를 돌려줘요. true로 설정하면 VM이 데드락 조건을 검사하지 않아요. 데드락을 시그널 같은 다른 수단으로 풀 수 있는 애플리케이션에서만 유용해요.
Thread.ignore_deadlock = true
queue = Thread::Queue.new
trap(:SIGUSR1){queue.push "Received signal"}
# 데드락을 무시하지 않으면 fatal error가 발생해요
puts queue.pop
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
list → array
실행 가능(runnable)하거나 정지된 모든 스레드의 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>
main → thread
메인 스레드를 돌려줘요.
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가 발생해요.
pass → nil
스레드 스케줄러에 실행을 다른 스레드로 넘기라는 힌트를 줘요. 실행 중인 스레드가 실제로 바뀔지는 OS와 프로세서에 따라 달라요.
pending_interrupt?(error = nil) → true/false
비동기 큐가 비어 있는지 여부를 돌려줘요. ::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
...
# 인터럽트를 호출할 안전 지점에 도달
if Thread.pending_interrupt?
Thread.handle_interrupt(Object => :immediate){}
end
...
end
}
}
...
th.raise # 스레드 중단
이 예시는 비동기 인터럽트를 피하기 위해 다음과 같이 쓸 수도 있어요.
flag = true
th = Thread.new{
Thread.handle_interrupt(RuntimeError => :on_blocking){
while true
...
# 인터럽트를 호출할 안전 지점에 도달
break if flag == false
...
end
}
}
...
flag = false # 스레드 중단
report_on_exception → true or false
전역 "report on exception" 조건의 상태를 돌려줘요. 기본값은 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'
이건 스레드의 오류를 일찍 잡기 위한 것이에요. 어떤 경우엔 이 출력이 원치 않을 수도 있는데, 여러 방법으로 피할 수 있어요.
- 예외가 의도된 게 아니면, 예외의 원인을 고쳐 더 이상 발생하지 않게 하는 게 최선이에요.
- 예외가 의도된 거라면, 스레드를 죽게 두기보다 발생 지점 가까이에서 rescue하는 게 나을 수 있어요.
Thread#join이나Thread#value로 반드시 join한다는 보장이 있다면, 스레드를 시작할 때Thread.current.report_on_exception = false로 이 보고를 꺼도 안전해요. 다만 이러면 예외를 훨씬 나중에 처리하거나, 부모 스레드가 막히는 등으로 스레드가 결코 join되지 않으면 아예 처리하지 않을 수도 있어요.
report_on_exception= boolean → true or 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
stop → nil
현재 스레드 실행을 멈추고 "sleep" 상태로 만든 뒤, 다른 스레드 실행을 예약해요.
a = Thread.new { print "a"; Thread.stop; print "c" }
sleep 0.1 while a.status!='sleep'
print "b"
a.run
a.join
#=> "abc"
인스턴스 메서드 (Public Instance Methods)
thr[sym] → obj or nil
Attribute Reference - fiber-local 변수의 값(명시적으로 Fiber 안이 아니면 현재 스레드의 루트 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#[]=은 thread-local이 아니라 fiber-local이에요. 이 혼동은 Ruby 1.8에는 없었는데, fiber는 Ruby 1.9부터 생겼기 때문이에요. Ruby 1.9는 동적 스코프(dynamic scope)를 위한 다음 관용구를 지키기 위해 이 메서드들이 fiber-local로 동작하도록 선택했어요.
def meth(newvalue)
begin
oldvalue = Thread.current[:name]
Thread.current[:name] = newvalue
yield
ensure
Thread.current[:name] = oldvalue
end
end
이 관용구는 메서드가 thread-local이고 주어진 블록이 fiber를 전환하면 동적 스코프로 동작하지 않을 수 있어요.
f = Fiber.new {
meth(1) {
Fiber.yield
}
}
meth(2) {
f.resume
}
f.resume
p Thread.current[:name]
#=> fiber-local이면 nil
#=> thread-local이면 2 (값 2가 meth 메서드 밖으로 새어 나옴)
thread-local 변수는 thread_variable_get과 thread_variable_set을 참고하세요.
thr[sym] = obj → obj
Attribute Assignment - 심볼 또는 문자열로 fiber-local 변수 값을 설정/생성해요. Thread#[]도 참고하세요. thread-local 변수는 thread_variable_set과 thread_variable_get을 참고하세요.
abort_on_exception → true or false
이 thr의 thread-local "abort on exception" 조건 상태를 돌려줘요. 기본값은 false예요. 인스턴스 레벨 abort_on_exception=도 참고하세요. 모든 스레드에 설정하는 클래스 레벨 메서드도 있어요(::abort_on_exception 참고).
abort_on_exception= boolean → true or false
true로 설정하면 이 thr가 예외로 중단될 때 그 예외가 메인 스레드에서 다시 던져져요.
add_trace_func(proc) → proc
proc을 추적 핸들러로 추가해요. Thread#set_trace_func와 Kernel#set_trace_func를 참고하세요.
alive? → true or false
thr가 실행 중이거나 잠들어 있으면 true를 돌려줘요.
thr = Thread.new { }
thr.join #=> #<Thread:0x401b3fb0 dead>
Thread.current.alive? #=> true
thr.alive? #=> false
stop?와 status도 참고하세요.
backtrace → array or nil
대상 스레드의 현재 backtrace를 돌려줘요.
backtrace_locations(*args) → array or nil
대상 스레드의 실행 스택을 backtrace location 객체 배열로 돌려줘요. Thread::Backtrace::Location을 참고하세요. Kernel#caller_locations처럼 동작하되 특정 스레드에 적용돼요.
exit → thr (terminate, kill 별칭)
thr를 종료하고 다른 스레드 실행을 예약한 뒤, 종료된 Thread를 돌려줘요. 메인 스레드나 마지막 스레드면 프로세스를 종료해요. 수신자가 현재 실행 중인 스레드와 다르면 호출자는 스레드가 종료될 때까지 기다리지 않아요. 종료는 비동기적이라, 스레드는 종료되기 전에 Ruby 코드를 조금 더 실행할 수 있어요.
fetch(sym) → obj / fetch(sym) { } → obj / fetch(sym, default) → obj
주어진 키의 fiber-local을 돌려줘요. 키를 찾을 수 없으면 여러 옵션이 있어요. 다른 인자 없이 호출하면 KeyError를 던지고, default가 주어지면 그것을 돌려주며, 선택 코드 블록이 주어지면 그것을 실행하고 결과를 돌려줘요. Thread#[]와 Hash#fetch를 참고하세요.
group → thgrp or nil
주어진 스레드를 담은 ThreadGroup을 돌려줘요.
Thread.main.group #=> #<ThreadGroup:0x4029d914>
join → thr / join(limit) → thr
호출 스레드는 실행을 멈추고 이 thr을 실행해요. thr이 끝나거나 limit초가 지날 때까지 반환하지 않아요. 제한 시간이 지나면 nil, 아니면 thr을 돌려줘요. join하지 않은 스레드는 메인 프로그램이 종료될 때 종료돼요.
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 # 스레드 x가 끝나게 하고, a는 종료 시 kill됨
#=> "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...
key?(sym) → true or false
주어진 문자열(또는 심볼)이 fiber-local 변수로 존재하면 true를 돌려줘요.
me = Thread.current
me[:oliver] = "a"
me.key?(:oliver) #=> true
me.key?(:stanley) #=> false
keys → array
fiber-local 변수 이름(심볼)들의 배열을 돌려줘요.
thr = Thread.new do
Thread.current[:cat] = 'meow'
Thread.current["dog"] = 'woof'
end
thr.join #=> #<Thread:0x401b3f10 dead>
thr.keys #=> [:dog, :cat]
kill → thr (terminate, exit 별칭)
thr를 종료하고 다른 스레드 실행을 예약한 뒤, 종료된 Thread를 돌려줘요. 자세한 내용은 exit 참고.
name → string / name=(name) → string
name은 스레드의 이름을 보여줘요. name=은 Ruby 스레드에 주어진 이름을 설정해요. 일부 플랫폼에서는 그 이름을 pthread 및/또는 커널에도 설정할 수 있어요.
native_thread_id → integer
Ruby 스레드가 쓰는 네이티브 스레드 ID를 돌려줘요. ID는 OS에 따라 달라요(pthread_self(3)이 돌려주는 POSIX 스레드 ID가 아니에요).
- Linux에서는
gettid(2)가 돌려주는 TID. - macOS에서는
pthread_threadid_np(3)이 돌려주는 시스템 전역 고유 정수 ID. - FreeBSD에서는
pthread_getthreadid_np(3)이 돌려주는 고유 정수 ID. - Windows에서는
GetThreadId()가 돌려주는 스레드 식별자. - 그 외 플랫폼에서는
NotImplementedError를 던져요.
NOTE: 스레드가 아직 네이티브 스레드와 연결되지 않았거나 이미 분리됐다면 nil을 돌려줘요. Ruby 구현이 M:N 스레드 모델을 쓰면 타이밍에 따라 ID가 바뀔 수 있어요.
pending_interrupt?(error = nil) → true/false
대상 스레드의 비동기 큐가 비어 있는지 여부를 돌려줘요. error를 주면 그 error 타입의 지연 이벤트만 검사해요. ::pending_interrupt?를 참고하세요.
priority → integer
thr의 우선순위를 돌려줘요. 기본값은 새 스레드를 만든 현재 스레드에서 상속받거나, 최초 메인 스레드라면 0이에요. 우선순위가 높은 스레드는 낮은 스레드보다 더 자주 실행돼요(낮은 스레드도 실행될 수 있어요). Ruby 스레드 스케줄러에 대한 힌트일 뿐이며 일부 플랫폼에서는 무시될 수 있어요.
Thread.current.priority #=> 0
priority= integer → thr
thr의 우선순위를 integer로 설정해요. 우선순위가 높은 스레드는 낮은 스레드보다 더 자주 실행돼요(낮은 스레드도 실행될 수 있어요). 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
raise(exception, message = exception.to_s, backtrace = nil, cause: $!) / raise(message = nil, cause: $!)
주어진 스레드에서 예외를 던져요. 호출자는 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
report_on_exception → true or false
이 thr의 thread-local "report on exception" 조건 상태를 돌려줘요. Thread를 만들 때의 기본값은 전역 플래그 Thread.report_on_exception의 값이에요.
report_on_exception= boolean → true or false
true로 설정하면 예외가 이 thr을 죽일 때 $stderr에 메시지가 출력돼요. ::report_on_exception을 참고하세요.
run → 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
wakeup 인스턴스 메서드도 참고하세요.
set_trace_func(proc) → proc / set_trace_func(nil) → nil
_thr_에 proc을 추적 핸들러로 설정하거나, 파라미터가 nil이면 추적을 비활성화해요. Kernel#set_trace_func을 참고하세요.
status → string, false or 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? 인스턴스 메서드도 참고하세요.
stop? → true or false
thr이 죽었거나 잠들어 있으면 true를 돌려줘요.
a = Thread.new { Thread.stop }
b = Thread.current
a.stop? #=> true
b.stop? #=> false
alive?와 status도 참고하세요.
thread_variable?(key) → true or false
주어진 문자열(또는 심볼)이 thread-local 변수로 존재하면 true를 돌려줘요.
me = Thread.current
me.thread_variable_set(:oliver, "a")
me.thread_variable?(:oliver) #=> true
me.thread_variable?(:stanley) #=> false
이것들은 fiber-local 변수가 아니에요는 점에 주의하세요. Thread#[]와 thread_variable_get을 참고하세요.
thread_variable_get(key) → obj or nil
설정된 thread-local 변수의 값을 돌려줘요. 이것들은 fiber-local 값과 다르다는 점에 주의하세요. fiber-local 값은 Thread#[]과 Thread#[]=을 참고하세요. Thread-local 값은 스레드를 따라 다니며, fiber를 존중하지 않아요.
Thread.new {
Thread.current.thread_variable_set("foo", "bar") # thread local 설정
Thread.current["foo"] = "bar" # fiber local 설정
Fiber.new {
Fiber.yield [
Thread.current.thread_variable_get("foo"), # thread local 가져오기
Thread.current["foo"], # fiber local 가져오기
]
}.resume
}.join.value # => ['bar', nil]
thread local은 "bar"(fiber local은 nil)임을 볼 수 있어요. fiber는 같은 스레드에서 실행되므로 thread local 값에 접근할 수 있어요.
thread_variable_set(key, value)
key를 value로 thread-local에 설정해요. 이들은 스레드에 local이지 fiber에 local이 아니라는 점에 주의하세요.
thread_variables → array
thread-local 변수 이름(심볼)들의 배열을 돌려줘요.
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]
이것들은 fiber-local 변수가 아니라는 점에 주의하세요.
to_s → string (inspect 별칭)
_thr_의 이름, id, 상태를 문자열로 덤프해요.
value → obj
join을 써서 thr이 끝나길 기다리고, 그 값 또는 스레드를 종료시킨 예외를 돌려줘요.
a = Thread.new { 2 + 2 }
a.value #=> 4
b = Thread.new { raise 'something went wrong' }
b.value #=> RuntimeError: something went wrong
wakeup → thr
주어진 스레드를 스케줄링 대상으로 표시해요. 다만 여전히 I/O에 블록될 수 있어요. NOTE: 이 메서드는 스케줄러를 호출하지 않아요. run을 참고하세요.
c = Thread.new { Thread.stop; puts "hey!" }
sleep 0.1 while c.status!='sleep'
c.wakeup
c.join
#=> "hey!"