오퍼레이팅 시스템 [12] - 생산자/소비자 문제, 독자/저자 문제

오퍼레이팅 시스템 [12] - 생산자/소비자 문제, 독자/저자 문제

상호배제와 조건 동기화로 Producer/Consumer, Reader/Writer Problem 을 해결해보자


생산자 / 소비자 문제

  • Producer/Consumer Problem으로 널리 알려져있는 문제를 해결해보자.
    • Bounded Buffer Prob으로도 알려져 있다.
    • 상호배제조건동기화를 활용해야 한다.
    • 공유자원인 Buffer 존재
  • 유한 Buffer 일때 (생산자, 소비자 제약) :
    • 생산자역할을 하는 Thread
      • 조건 : Buffer가 꽉차면 대기해야한다.
    • 소비자역할을 하는 Thread
      • 조건 : Buffer가 비면 대기해야한다.
  • 무한 Buffer 일때 (소비자 제약):
    • 생산자역할을 하는 Thread
      • 조건없이 계속 생산가능
    • 소비자역할을 하는 Thread
      • 조건 : Buffer가 비면 대기해야한다.

CV를 활용한 해결

무한 Buffer의 경우

  • 소비자에게만 제약이 걸려있는 경우로, producer()wait()함수가 필요하지 않다.
//file: "생산자 Thread"
void producer(){
  while(1){
    pthread_mutex_lock(&m);//Mutual exclude
    put(value); //Buffer에 물건을 하나 채운다
    pthread_cond_signal(&c); //소비자를 깨운다
    pthread_mutex_unlock(&m);
  }
}
//file: "소비자 Thread"
void consumer(){
  while(1){
    pthread_mutex_lock(&m);//Mutual exclude
    while(count==0){
      pthread_cond_wait(&c, &m);//Buffer에 물건이 없으면 대기(소비자 제약)
    }
    get();//Buffer에서 물건을 하나 소비한다.
    pthread_mutex_unlock(&m);
  }
}

유한 Buffer의 경우

//file: "생산자 Thread"
void producer(){
  while(1){
    pthread_mutex_lock(&m);//Mutual exclude
    while(count==MAX){
      pthread_cond_wait(&empty, &m);//Buffer에 꽉차있으면 대기(생산자 제약)
    }
    put(value);
    pthread_cond_signal(&fill); //채웠다고 소비자를 깨운다
    pthread_mutex_unlock(&m);
  }
}
//file: "소비자 Thread"
void consumer(){
  while(1){
    pthread_mutex_lock(&m);//Mutual exclude
    if(cound==0){
      pthread_cond_wait(&fill, &m);//Buffer에 물건이 없으면 대기(소비자 제약)
    }
    get();//Buffer에서 물건을 하나 소비한다.
    pthread_cond_signal(&empty);//빈자리가 있다고 생산자를 깨운다
    pthread_mutex_unlock(&m);
  }
}

차이점

  • 무한버퍼의 경우 소비자 Thread 에게만 제약이 있으므로, 1개pthread_cond_t 객체만 사용해도 되지만, (c)

    유한버퍼의 경우 생산자와 소비자 Thread모두 제약이 있으므로 2개pthread_cond_t 객체를 사용하여 각각의 전용 wait(), signal()함수를 사용해야한다. (fill, empty)

    그렇지 않을시 다수의 생산자, 소비자가 있는경우 signal()함수로 잘못된 Thread를 깨우는 경우가 발생한다.

  • IF문이 아니라 while문을 사용하여 State variable(조건변수)의 상태를 확인하는 이유
    if문을 사용하면, 조건에 부합하여 wait()함수를 통해 sleep된 후, signal()함수에 의해 깨어난 다음, Condition을 다시 Check하지 않고 그대로 실행하므로 만약에 조건변수가 바뀌었어도 이를 알아차리지 못하고 실행되어 오류가 발생할 수 있다.
    • 따라서! while문을 사용하여 깨어난 후에, 조건변수가 바뀌지않았더라도, 다시 한번 RE-CHECKING 해줌으로서 일어날 수 있는 오류를 방지한다!

Semephore를 활용한 해결

  • 앞서 알아봤던 것 처럼 Semaphore는 상호배제뿐만이 아니라 Semaphore객체의 값을 설정함으로써 조건동기화의 기능도 할 수 있다.

  • SemaphoreCondition Variable로 사용하는 경우
    • 세마포어 함수 sem_t s;
      1. sem_wait(&s) : 세마포어의 값을 하나 빼주고, 만약 값이 음수라면 block하고 queue로 들어가고, 양수라면 실행됨
      2. sem_post(&s) : signal()과 같으며 세마포어의 값을 하나 더해주고, queue에 있는 스레드를 하나 실행한다.
      3. sem_init(&s,0,1): 세마포어 초기화. 두번째값은 세마포어의 프로세스간 공유여부, 세번째값이 세마포어의 초기 값을 설정한다.
  • 필요한 세마포어 객체들
    • empty : 세마포어 값을 유한버퍼의 최대크기로 초기화한다. 간단하게 현재 buffer에 비어있는 공간의 크기라고 생각한다.
    • fill : 세마포어의 값을 0으로 초기화한다. 간단하게 현재 buffer에 차있는 공간의 크기라고 생각한다.
    • m : binary 세마포어로 값을 1로 초기화한다. 상호배제를 위해 쓰인다.

//file: "생산자 Thread"
void producer(){
  sem_wait(&empty);//생산할때마다 세마포어값(남아있는 공간)을 1씩 감소시키며, 음수일 경우 block후 queue로 들어간다.
  sem_wait(&m);//생산을 위해 buffer에 접근할때를 위한 상호배제(m은 binary semaphore객체)
  put(value);
  sem_post(&m);
  sem_post(&fill);//채우고 세마포어값(차있는 공간)을 1씩 증가시키며, 소비자가 실행될 수 있도록함
}
//file: "소비자 Thread"
void consumer(){
  sem_wait(&fill);//소비할때마다 세마포어값(비어있는 공간)을 1씩 감소시키며, 음수일 경우 block후 queue로 들어간다.
  sem_wait(&m);
  get();
  sem_post(&m);
  sem_post(&empty);//비우고 세마포어값(남아있는 공간)을 1씩 증가시키며 생산자가 실행될 수 있도록함
}

독자 / 저자 문제

독자 우선순위

저자 우선순위