광고
더치페이 대신 랜덤빵 하십쇼🚀
iOS 앱 랜덤빵
다운로드 부탁 더립니다..
루비의 스레드에 대해 이야기 하기 전에.
Many(Thread) to One(CPU) 모델로 설계된 스레드.
VM이나 Lribrary등 에서 개발자가 통제 가능하다.
그린 스레드가 아무리 많아도 네이티브 스레드는 단 한개만 생성된다.
→ CPU 하나만 사용한다.
멀티 코어 CPU 환경의 장점을 살리지 못함.
하지만 장점이 있다.
싱글 코어 환경에서 10 Native thread VS 10 green thread 에 대해 생각해 보자.
네이티브 10개 사용 → 네이티브 10개 생성
그린 10개 사용 → 네이티브 1개 생성
→ CPU의 입장에서는 네이티브 10개 쓰는것 보다 1개쓰는게 성능이 좋다.
why? 네이티브 스레드가 1개 뿐이기 때문에, 동기화 할 공유 자원이 없다.
그래서 싱글코어, 싱글 스레드 환경에서는 그린 스레드가 성능이 더 좋다.
진짜 스레드.
OS단에서 관리된다.
non-green, kernel-level thread 라고도 함.
Many to Many 모델로 설계됨.
멀티 코어 환경에서 강점이 있다.
하지만,
여러 스레드를 사용 한다면
→ 스레드 사이에 공유되는 자원을 동기화 해야하는 이슈가 있다.
→ 그린 스레드에 비해 복잡한 동기화 문제가 있다.
동기화를 잘 하면 된다ㅎㅎ
ruby 1.8.7 까지는 그린스레드 방식이었음.
ruby 1.9 이후로 네이티브 OS 스레드를 사용함. 하지만 멀티 코어 환경이어도 싱글 프로세스 속에서만 동작함.
참고사항.
각 HTTP 요청이 별도의 스레드에서 처리 될 수 있고, 요청간에 많은 리소스를 공유하지 않기 때문에 스레드는 대부분의 경우 잘 작동합니다.
아래 코드에서는 race condition 이 발생한다
@count = 0
def read_count
@count
end
def add_value value
@count = value
end
100.times.map do
Thread.new do
100_000.times do
value = read_count
value = value + 1
add_value value
end
end
end.each(&:join)
puts @count
# 4214508
컨텍스트 스위칭이 일어나기 떄문.
루비에서는 메소드를 호출 하거나, 메소드로부터 리턴 될때 컨텍스트 스위칭이 일어남.
스레드는 컨텍스트를 변경한다.
스레드가 멈추면, 그 상태와 컨텍스트가 저장되고 다른 스레드가 CPU 사이클을 사용할 수 있게 된다.
@count = 0
100.times.map do
Thread.new do
100_000.times do
value = @count
value = value + 1
@count = value
end
end
end.each(&:join)
puts @count
# 10000000
이렇게 하면 컨텍스트 스위칭이 일어나지 않고, 경쟁 상태도 없다.
하지만 대부분읭 경우에 메소드호출을 많이 할 수 밖에 없다.
컨텍스트 스위칭이 일어나면서 공유자원의 동시 사용을 막기 위해서는?
뮤텍스 세마포 등을 이용해야 한다.
@count = 0
mutex = Mutex.new
def read_count
@count
end
def add_value value
@count = value
end
100.times.map do
Thread.new do
100_000.times do
mutex.synchronize do
value = read_count
value = value + 1
add_value value
end
end
end
end.each(&:join)
puts @count
# 10000000
뮤텍스를 이용한 코드
뮤텍스를 통해 공유 자원에 락을 걸면서 상호 배제를 만들어 낸다.
뮤텍스는 단 하나의 스레드만 공유자원에 접근 가능하게 한다. 다른 스레드들은 공유자원에 접근중인 스레드가 작업이 끝날때 까지 기다린다(블락됨).
이러한 특성 때문에**_ 뮤텍스는 공유 자원의 원자성을 보장하기 위해 사용_**한다.
멀티 스레드 프로그래밍은 두개 이상의 스레드가 동시 연산을 하면서 프로세스(소프트웨어)의 성능을 높이기 위해 사용한다.
그런데 뮤텍스는 락을 걸기 때문에, 잘 사용하지 않으면 병목구간이 될 수 있다.
가능하면 뮤텍스 구간을 최소화 하는게 좋다.
실행시간을 대충 측정해봤다.
0.618842 메소드 없이
0.975594 메소드 콜
51.195683 뮤텍스 사용
0.975366 뮤텍스 블럭을 100_100.time 블럭 안으로 이동
뮤텍스 락을 적절한 위치에서 사용하면 성능을 높일 수 있다.
조건에 따라 뮤텍스 락과 언락을 수행하는 방법.
why? 공유자원을 관리가 뮤텍스로 충분하지 않을 수 있다.
뮤텍스를 얻고 작업을 진행하는 중, 다른 스레드의 자원이 준비 되는걸 기다려야 할 때가 있다. 준비 될때까지 다른 스레드들은 블락될 것이다.
이때, Condition Variable(CV)을 사용한다.
사용방식
→ 한 스레드가 뮤텍스를 얻고 작업을 진행
→ 다른 자원을 기다리기 전, 뮤텍스 반환
→ 자원을 얻으면, 다시 뮤텍스를 얻음(이때, 다른 스레드와 경쟁하기 때문에, 일정 시간 대기 상태에 있을 수 있다.)
→ 작업 끝나면, 뮤텍스 반환
mutex = Mutex.new
cv = ConditionVariable.new
count = 0
a = Thread.new {
mutex.synchronize {
puts "A: #{count}"
cv.wait(mutex) if count == 0
puts "A: #{count}"
}
}
b = Thread.new {
mutex.synchronize {
puts "B: #{count}"
count += 1
cv.signal
puts "B: #{count}"
}
}
[a, b].each(&:join)
# A: 0
# B: 0
# B: 1
# A: 1
스레드를 종료함
t = Thread.new { ... }
Thread.kill(t)
# t.exit과 동일
스레드의 상태를 리턴
t = Thread.new { sleep }
t.status # => "sleep"
t.exit
t.status # => false
현재 실행 중인 스레드를 리턴
Thread.current #=> #<Thread:0x401bdf4c run>
더치페이 대신 랜덤빵 하십쇼🚀
iOS 앱 랜덤빵
다운로드 버탁 더립니더..