티스토리 뷰
운영체제: 가장 쉬운 세가지 이야기 - Operating Systems: Three Easy Pieces를 바탕으로 작성하였습니다.
컨디션 변수
- 컨디션 변수는 특정 조건이 만족되기를 기다리며 대기하는 "큐"다.
- 다른 쓰레드가 시스템의 상태를 변경시키고, 이에 따라 조건이 충족되면 대기 중인 쓰레드에게 signal을 발생시킨다.
- 쓰레드 실행 전 특정 조건의 만족 여부를 검사하는 경우는 흔하다. 이 때 컨디션 변수를 사용할 수 있다.
- 회전 대기(while과 flag로 구현)는 단순하나 낭비가 있다.
동작
*어떤 쓰레드가 먼저 실행될지는 비결정적이라는 사실을 기억한다.
1. 부모 쓰레드가 먼저 실행되는 경우
1 thr_join() 실행, done이 1이 아니면 wait() 실행, 이 때 보유 중인 락을 해제하고 대기에 들어간다.
2 자식 쓰레드가 실행되고, child()를 실행한 후 exit()를 실행한다. 락 획득 후 done = 1로 할당하고 signal을 보낸 직후 락을 해제한다.
3 signal에 의해 대기 중인 쓰레드가 깨어남과 락을 획득하고, 락 해제 후에 종료한다.
2. 자식 쓰레드가 먼저 실행되는 경우
1 done = 1을 설정한다. signal을 보내도 깨울 쓰레드가 없다. 종료한다.
2 부모 쓰레드가 실행된다. done이 1이므로 while stmt 안으로 들어가지 않는다. 따라서 wait()을 실행하지 않고 종료한다.
생산자/소비자(유한 버퍼)문제
- 유한 버퍼의 흔한 예로, 멀티 쓰레드 웹 서버의 경우, 생산자 쓰레드가 다수의 HTTP 요청을 받아 작업 큐(유한 버퍼)에 저장한다. 이후 소비자 쓰레드가 이 큐에서 요청들을 꺼내 처리한다.
- 유한 버퍼는 "공유 자원"이다. 따라서 여러 쓰레드간 경쟁을 방지하기 위해 동기화가 필요하다.
- put(), get()과 함께 구현된다. 단순하다. 생산자 쓰레드가 put으로 버퍼에 데이터를 담고, 소비자 쓰레드가 get으로 데이터를 수거해 간다.
- 생산자 쓰레드는 put()으로 버퍼에 데이터 넣기 전 버퍼가 비어있는지 확인한다.
- 소비자 쓰레드는 get()으로 버퍼에서 데이터를 획득하기 전 버퍼가 차있는지 확인한다.
불완전한 해답
- 락과 컨디션 변수를 함께 사용한다.
- 생산자 쓰레드가 signal을 보내 소비자 쓰레드가 wait()에서 깨어나는(blocked -> ready) 시점과 이후 깨어난 쓰레드의 실행 시점에서의 버퍼의 상태가 다른 소비자 쓰레드의 실행으로 인해 달라질 수 있다.
아래의 방식으로 해결한다.
- Mesa semantic:
- if를 while로 바꾸면 해결된다. 쓰레드가 조건을 재검사하지 않기 때문에 발생하는 문제이기 때문이다.
- 대부분의 시스템에서 사용된다.
- Hoare semantic
- 또 다른 문제: 누가 어떤 쓰레드를 깨워야 할까?
- 컨디션 변수가 하나이기 때문에 발생하는 문제
- 소비자 쓰레드가 생산자 쓰레드를 깨워야하는데 signal 구현 특성 상 누구를 깨울지 지정할 수 없으므로, 다른 소비자 쓰레드를 깨울 수 도 있다. 이 경우 다른 쓰레드를 "깨운" 쓰레드는 대기에 들어가고, "깨워진" 쓰레드도 버퍼를 확인하고 비어있기 때문에 대기 상태에 들어간다. 모든 쓰레드가 다 sleep 상태에 들어가는 문제가 발생한다.
단일 버퍼 생산자/소비자 해법
- 두 개의 컨디션 변수를 사용한다.
- 소비자는 생산자, 생산자는 소비자에게만 signal()을 보낼 수 있게 된다.
올바른 생산자/소비자 해법
- 버퍼 크기를 늘린 버전이다.
- 단일 생산/소비자의 경우, 여러 개의 생산/소비자의 경우 모두 병행성이 증가한다.
포함 조건(Covering Condition)
- 예를 들어 다수의 쓰레드가 메모리 공간의 발생을 대기하고 있는 경우 어떤 쓰레드를 깨워야 하는가? 사실 어떤 쓰레드를 깨울지 지정하는 것도 불가능하다.
T_a가 100 bytes, T_b가 10 bytes를 요청하고 대기 중이라고 하자, T_c가 free()를 통해 50 bytes를 방출하면 T_b에게 signal이 가야하는 것이 맞나, 이것은 보장될 수 없다.
-> 모두에게 signal 보내는 방식으로 해결한다.
- 조건 만족하는 쓰레드만 락을 획득하게 된다.
- 문제는 불필요한 문맥 전환 비용이 발생하게 된다는 점이다.
★ 멀티 쓰레드 프로그램에서 조건 검사 시 if가 아닌 while을 사용한다. signal 전달 과정에서 경쟁 조건에 의해 발생할 수 있는 spurious wakeup 문제에 대비하기 위해서다.
'운영체제(Three Easy Pieces)' 카테고리의 다른 글
29. 락 기반의 병행 자료구조 (0) | 2021.06.13 |
---|---|
29. 락 (0) | 2021.06.13 |