동기화 응용

2023. 9. 26. 06:07전공/운영체제

컨디션 변수

스레드 실행 중간 조건 검사

  • 부모 스레드가 작업을 시작하기 전 자식 스레드가 작업을 끝냈는지 알고 싶다면?
  • 회전(spin) 기반 공유 변수 활용
  • but 바쁜 대기 문제가 남음

컨디션 변수(Conditional variable)

  • 일종의 큐 자료 구조
  • 어떤 실행의 상태(또는 어떤 조건)가 원하는 것과 다를 때, 조건이 참이 되기를 기다리며 스레드가 대기할 수 있는 큐
  • 스레드의 바쁜 대기 문제를 해결할 수 있음

pthread_cond_t c;

  • 컨디션 변수 c 정의]

pthread_cond_wait(pthread_cond_t *c, pthread_mutex *m)

  • 스레드가 스스로를 재우기(sleep) 위하여 호출
  • mutex lock변수 m의 락을 해제하고, 스레드를 잠재움
  • 다른 스레드로부터 시그널을 받으면 락을 다시 걸고 리턴됨

pthread_cond_signal(pthread_cond_t *c)

  • 조건이 참이 되길 기다리며 잠자고 있던 스레드를 깨우기 위해 호출

만약 상태 변수 done을 정의하지 않는다면?

for(int i = 0; i < 1000000; i++); 를 추가하게 되면 thr_join() 함수가 실행되기 전에 c스레드가 child함수를 실행시키면서 thr_exit()함수가 먼저 실행되게 되어 pthread_cond_signal(&c)를 실행하지만 c는 sleep상태가 아니므로 그냥 지나가게 된다. for문이 끝난 후 thr_join()함수가 실행되어 pthread_cond_wait(&c, &m)이 실행되고 c가 sleep상태가 되지만 pthread_cond_signal(&c) 함수가 이미 실행된 후이므로 sleep 상태의 c를 깨워줄 signal함수가 없어 c는 계속 sleep상태로 머문다. c가 계속해서 sleep상태이기 때문에 main 함수는 thr_join() 이후의 코드들을 실행할 수 없어 위와 같은 문제가 발생한다.

생산자/소비자(유한버퍼) 문제

생산자/소비자(producer/consuemr) 문제

  • 유한 버퍼(bounded buffer) 문제라고도 함
  • 여러 개의 생산자 스레드와 소비자 스레드가 존재
  • 생산자 스레드는 데이터를 만들어서 버퍼에 넣음
  • 소비자 스레드는 버퍼로부터 데이터를 꺼내어 사용
  • 예시 1: 멀티 스레드 웹 서버
    • 생산자 스레드는 HTTP request를 작업 큐(유한버퍼)에 삽입
    • 소비자 스레드는 큐에서 request를 꺼내어 처리
  • 예시 2 : 파이프 명령 grep foo file.txt | wc -l
    • 두 개의 프로세스 grep과 w 가 병행 실행
    • grep의 표준 출력 결과는 wc의 표준 입력으로 전달
    • grep은 생산자, wc는 소비자

put() 및 get() 루틴

  • put() : 공유 버퍼에 값을 넣는 루틴
  • get(0 : 공유 버퍼로부터 값을 꺼내는 루틴

임계영역을 고려하지 않은 생산자/소비자 구현 코드

컨디션 변수를 적용한 생산자/소비자 구현 코드

소비자 스레드가 2개 이상이라면?

if문 대신 while문을 적용한 생산자/소비자 구현 코드

소비자 스레드가 2개이므로 하나의 소비자 스레드가 wait상태인 다른 소비자 스레드에게 pthread_cond_signal() 함수를 사용하여 깨울 수 있고 if문을 사용하여 count 상태를 한 번만 확인하기 때문에 위와 같은 문제가 생긴다. 예를 들어 c1과 c2스레드가 wait상태일 때 p가 뮤텍스 락을 얻고 put()을 수행하여 count = 1이 되고 pthread_cond_signal(&cond, &mutex)을 호출하여 c1이 깨어난다면, c1은 get()을 수행하여 count = 0이 되고 pthread_cond_signal(&cond ,&mutex)을 호출하여 c2가 깨어나게 된다. 이때 c2에서 if(count == 0) 조건문은 이미 조건 확인이 끝난 상태이므로 c2는 if문을 빠져나와 get()을 수행하게 되고 이 때 count = 0인 상태이므로 assert(count == 1)을 했을 때 에러가 발생하게 된다.

두 개의 컨디션 변수를 사용하는 생산자/소비자 구현 코드

세마포어 구현

세마포어(Semaphore)

  • 정수 값을 갖는 객체
  • 두 개의 루틴, sem_wait()와 sem_post()로 조작
  • 헤더파일 : semaphore.h

sem_t s;

세마포어 변수 s

int sem_wait(sem_t *s)

  • 세마포어 변수 s를 1만큼 감소
  • 세마포어 변수 s가 음수라면 wait

int sem_post(sem_t *s)

  • 세마포어 변수 s를 1만큼 증가
  • 대기 중인 스레드가 1개 이상 있다면, 스레드 하나를 깨움(wake up)

이진 세마포어(락)

  • 세마포어 값은 0 또는 1이 됨
  • sem_wait() 함수와 sem_post() 함수로 임계영역을 둘러싸는 단순한 구조

 

  • 스레드 Trace : 세마포어를 사용하는 단일 스레드
  • 스레드 Trace : 세마포어를 사용하는 두 개의 스레드

조건 체크를 위한 세마포어

  • 특정 조건이 참이 될 때까지 대기하는 스레드 코드 구현
  • 세마포어를 컨디션 변수와 유산한 용도로 사용
  • 스레드 Trace : 자식 스레드를 기다리는 부모 스레드

Reader and Writer문제

Reader and Writer문제

  • 다수의 reader와 wrtier threads에 의해 데이터가 공유됨
  • 한 번에 오직 하나의 writer만이 공유 데이터에 접근 가능
  • 한번에 다수의 reader가 공유 데이터에 접근 가능
  • 공유 데이터에 임의의 reader가 접근한 경우 다른 reader 접근 가능
  • 공유 데이터에 임의의 reader가 접근한 경우 다른 writer 접근 불가

Reader and Writer를 위한 세마포어

  • sem_t lock
    • reader스레드가 writelock을 획득 혹은 해제하는 작업이 원자적(atomic)으로 수행되는 것을 보장하기 위한 락
  • sem_t writelock
    • 임계 영역에 진입한 스레드의 유무를 나타냄
    • 공유 데이터에 대해 한번에 하나의 writer스레드만 접근하도록 허용하는 락
    • writer스레드가 writelock을 획득하지 않은 상황에서는 공유 데이터에 대해 다수의 reader스레드가 접근할 수 있음
    • writer스레드가 writelock을 획득한 상황에서는 다른 write스레드 및 reader스레드 누구도 공유 데이터에 접근할 수 없음
  • int read_count
    • 공유 데이터에 접근한 reader스레드의 개수

 

'전공 > 운영체제' 카테고리의 다른 글

메인 메모리 관리  (0) 2023.09.26
데드락  (0) 2023.09.26
동기화(Synchronization)  (0) 2023.09.16
CPU 스케쥴링  (0) 2023.09.16
저수준에서의 프로세스 관리  (0) 2023.09.16