동시성 제어 문제( Concurrency - Control )의 대표적 예시
한정 버퍼 문제( Bounded-Buffer Problem )
。생산자 - 소비자 문제( Producer - Consumer Problem )
독자 - 저자 문제( Readers - Writers Problem )
。여러명의 독자와 저자들이 하나의 저장공간(버퍼)에 동시접근 시 발생하는 문제
철학자들의 저녁식사 문제( Dining - Philosophers Problem )
。OS상Deadlock상태를 설명하기위한 문제
한정 버퍼 문제( Bounded - Buffer Problem )
。대표적 예시 :생산자 - 소비자 문제OS - IPC
。Buffer Pool이 각각 1개의item을 포함하는n개의Buffer들로 구성
▶생산자 프로세스는소비자 프로세스를 위해full buffer제공
▶소비자 프로세스는생산자 프로세스를 위해empty buffer를 제공
한정버퍼문제코드예시
。생산자 M:소비자 N관계 에서의 단일Buffer Pool로 접근 시
▶Mutex또는Binary Semaphore로임계영역접근
- 초기화
。1개의Binary Semaphore와 2개의Counting Semaphore로 초기화
。Binary Semaphore:
mutex:Mutex기능으로서상호배제보장을 위한임계영역동기화 역할 수행
▶Buffer Pool에 동시접근을 방지하는Lock역할
▶Counting Semaphore가 아니므로 오직 단일프로세스만Buffer Pool로 접근
。Counting Semaphore:
empty:Buffer Pool에 구성된empty buffer수를 Counting
full:full buffer의 수를counting
ex )Empty Buffer로 구성된Buffer Pool에 한개의Empty Buffer에item을 넣는 경우
▶empty는Buffer Pool의 최대버퍼수n--
▶full은0++int n; semaphore mutex = 1; // // 임계영역 동기화 용도 semaphore empty = n; // Empty Buffer 카운트 semaphore full = 0; // Full Buffer 카운트
- 생산자 프로세스 구조
while (true){ // // Buffer Pool의 Empty Buffer에 item을 포함시키는 코드 // wait(empty); // Full Buffer로 꽉찬 경우 Wait Queue 대기 wait(mutex); // 다른 프로세스가 임계영역 존재 시 Wait Queue 대기 // // 임계영역 // Buffer Pool에 Buffer Pool를 추가하는 코드란 // Buffer Pool에 Empty Buffer가 있고 오직 한개의 프로세스만 접근 가능. // signal(mutex); // 프로세스 자원 반납 signal(full); // Full Buffer가 추가되었으므로 full++ }。
wait(empty):Buffer Pool에Empty Buffer가 존재하지 않는경우Wait Queue에서 대기
▶empty > 0을 만족할 경우wait(mutex)실행 및empty--
▶empty <= 0인 경우Full Buffer로 꽉찬 상태로서Wait Queue에서 대기
。wait(mutex): 단일프로세스만임계영역에 진입할 수 있도록 설정
▶ 다른프로세스가 이미임계영역에 진입하여mutex == 0인 경우Wait Queue에서 대기
▶Buffer Pool에Empty Buffer가 존재하고, 한개의프로세스만임계영역에 접근한 경우에만Buffer Pool에 접근하여Buffer를 작성
。signal(mutex):프로세스가 자원 반납 시 선언
▶mutex--
。signal(full):Buffer Pool에 1개의Full Buffer가 생겼으므로full++
- 소비자 프로세스 구조
while (true){ wait(full); // Full Buffer가 없는경우 Wait Queue 대기 wait(mutex); // 다른 프로세스가 임계영역 존재 시 Wait Queue 대기 // // 임계영역 // Buffer Pool의 한개의 Full Buffer에서 item 제거 // signal(mutex); // 프로세스 자원 반납 signal(full); // Empty Buffer 가 하나 생겼으므로 empty++ // // 버퍼에서 추출한 item을 활용하는 코드란 // }。
wait(full):Buffer Pool에서item을 추출할Full Buffer가 없는 경우Wait Queue에서 대기
▶full > 0을 만족할때까지Wait Queue에서 대기 및 만족 시full--후wait(mutex)실행
。signal(empty):Buffer Pool에Empty Buffer가 하나 생겼으므로empty++
。다음 대칭을 만족해야 정상적으로 구동
▶Timing Error의 위험 존재
한정버퍼문제코드
pthread예시
。semaphore활용활용
- 초기화
#include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <pthread.h> #include <semaphore.h> #define true 1 #define BUFFER_SIZE 5 // 공유자원 초기화 int buffer[BUFFER_SIZE]; pthread_mutex_t mutex; sem_t empty, full; int in = 0, out = 0;
- 생산자 스레드 수행함수
// 생산자 스레드 void *producer(void *param){ int item; while (true){ usleep((1 + rand() % 5) * 100000); item = 1000 + rand() % 1000; insert_item(item); // 임계영역 } }。
usleep을 통해0.1초 ~0.5초 사이를sleep후item을 랜덤으로 생성 후임계영역에서버퍼에 삽입
。임계영역void insert_item(int item){ sem_wait(&empty); pthread_mutex_lock(&mutex); buffer[in] = item; in = (in + 1) % BUFFER_SIZE; printf("생산자 : 삽입 %d\n", item); pthread_mutex_unlock(&mutex); sem_post(&full); }。
buffer pool내empty buffer존재 및 (empty > 0)임계영역의공유자원을 점유한스레드가 없는 경우 (mutex == 1)임계영역에 진입 및empty--후Empty Buffer에item추가
▶ 이후자원반납(mutex++) 및full++
- 소비자 스레드 수행함수
// 소비자 스레드 void *consumer(void *param){ int item; while (true) { usleep((1 + rand() % 5) * 100000); remove_item(&item); // 임계영역 } }。
usleep을 통해0.1초 ~0.5초 사이를sleep후임계영역에서버퍼의item을 삭제
。임계영역void remove_item(int *item){ sem_wait(&full); pthread_mutex_lock(&mutex); *item = buffer[out]; out = (out + 1) % BUFFER_SIZE; printf("소비자 : 삭제 %d\n:",*item); pthread_mutex_unlock(&mutex); sem_post(&empty); }。
buffer pool내full buffer존재 및 (full > 0)임계영역의공유자원을 점유한스레드가 없는 경우 (mutex == 1)임계영역에 진입 및full--후Full Buffer에서item추출 및 해당item의pointer( =out) 제거
▶ 이후자원반납(mutex++) 및empty++
- 메인 스레드
// 메인스레드 int main(int argc, char *argv[]){ int i , numOfProducers = 1, numOfConsumers = 1; pthread_t tid; pthread_mutex_init(&mutex, NULL); sem_init(&empty,0,BUFFER_SIZE); sem_init(&full, 0, 0); srand(time(0)); // 생산자 스레드 생성 for (i=0; i<numOfProducers ; i++) pthread_create(&tid,NULL,producer,NULL); // 소비자 스레드 생성 for (i=0; i<numOfConsumers ; i++) pthread_create(&tid,NULL,consumer,NULL); sleep(10); return 0; }。
Counting Semaphoreempty = 버퍼 총 갯수,full = 5로 초기화
。메인스레드에서 자식생산자 / 소비자 스레드생성 및Concurrent하게 실행
。sleep(10)을 통해 10초동안스레드작업 수행
。 5개의소비자 스레드와 5개의생산자 스레드가동기화를 유지하면서Concurrent하게 10초동안 단일Buffer pool에 접근하여Buffer의item을 삽입 및 삭제를 수행
▶Buffer Pool의Buffer수를 조정하거나,소비자또는생산자 스레드수를 조정하더라도동기화는 유지
JAVA예시
JAVA - Monitor로 생산자소비자 문제 해결
독자 - 저자 문제( Readers - Writers Problem )
。여러명의 독자와 저자들이 하나의 공유자원( ex.DB)에 동시접근 시 발생하는 동기화 문제
。생산자 - 소비자 문제와 다른점은독자 프로세스( =Reader)는 읽기만하고 (Read) ,작가 프로세스( =Writer)는 읽기 & 쓰기 (Write)를 둘다 수행
ex )DB에Concurrent하게 접근하여reader는SELECT를 통한 데이터 조회만 수행 및writer는 데이터의UPDATE를 통한 데이터 조회 및 수정을 수행
。DB에Reader - Reader가Concurrent하게Execute시동기화 문제는 발생하지 않는다.
。DB에Writer - Writer와Reader - Writer가Concurrent하게Execute시Race Condition발생
。독자 - 저자 문제의 해결방법 :Reader - Writer Locks
- Readers - Writers Problem의 유형
。독자 / 작가 프로세스간 우선순위로 인해Starvation이 다음 두가지 유형에서 발생할 수 있음
- First Readers-Writers Problem :
。Reader가임계영역에서 데이터를 읽는 과정에서Writer가 진입대기중이더라도 다른Reader가 진입대기할 필요는 없다
▶Reader - Reader는Race Condition이 발생하지 않으므로
。 해당 문제에서Writer가 수 많은Reader에 의해 무한정 대기하는Starvation문제가 발생할 수 있음
▶ 마지막Reader가 공유자원을Release해야Writer가임계영역에 진입가능
▶임계영역에 대해 대기중인Reader와Writer에게 서로 공평하게우선순위를 부여해야함.
- Second Readers-Writers Problem :
。Writer가임계영역에 진입하려고 대기중일때, 새로운Reader가임계영역에 접근하지 못하는 문제
。해당 문제에서Reader가Starvation이 발생할 수 있음
- Readers-Writers Problem 코드예시
。독자 M:작가 N프로세스 간 단일 공유자원(DB)에 접근하는 경우
▶Binary Semaphore사용
。Reader와Writer는DB에Concurrent하게 접근.
。특정Writer가임계영역에서Execute중이고,n개의Reader가Wait Queue에서 대기중일때,
▶1개의Reader는rw_mutex의Wait Queue에서 대기하여Reader와Writer간동기화
▶n - 1개의Reader는mutex의Wait Queue에서 대기하여Reader간동기화
- 초기화
。Binary Semaphore를 사용하므로 오직 단일프로세스가임계영역에 진입하여 단일자원에 접근가능.semaphore rw_mutex = 1; // reader 와 writer가 공유해서 사용 semaphore mutex = 1; // 임계영역 동기화 용도 int read_count = 0; // reader 갯수。2개의
Binary Semaphore와 1개의int사용
。Binary Semaphore
rw_mutex:임계영역동기화 용도Lock
▶Reader와Writer모두 참조하며 다른Writer의 접근을Block하여상호배제보장
mutex:임계영역동기화 용도Lock
▶read_count를 update 시 다른Reader의 접근을Block하여상호배제를 보장
read_count:Reader의 갯수
▶공유자원을 읽고있는Reader의 수를 측정
▶read_count == 0인 경우Writer진입가능
독자 프로세스( Reader Process )while (true){ wait(mutex); // Critical Section read_count++; if ( read_count == 1 ){ // Reader가 임계영역에 있는 경우 // Writer Block wait(rw_mutex); } signal(mutex); // // Reading 코드란 // wait(mutex); // Critical Section // Reader를 임계영역에서 해제 read_count--; if ( read_count == 0 ){ // Reader가 임계영역에 없는 경우 // Writer 임계영역 진입 signal(rw_mutex); } signal(mutex); }。첫번째
wait(mutex)를 통해 단일Reader또는Writer의임계영역진입 후read_count++후 권한 반납후 Reading 작업 수행
▶ 첫번째Reader가Execute된 경우read_count == 1이 되고, 이때wait(rw_mutex);를 통해 다른Writer의임계영역접근을 제한
。Reading이 끝난 후 두번째wait(mutex)를 통해 단일Reader또는Writer는임계영역진입 후read_count--수행 후 권한 반납
。read_count == 0인 경우
▶Reader와Writer가임계영역에서 없는경우에만Reader에서signal(rw_mutex)를 통해Writer가임계영역에 진입
。Writer가signal(rw_mutex)실행 시 대기중인 복수의Reader또는 단일Writer중 하나를스케쥴러에 의해 선택하여 실행을 재개
▶스케쥴러가Reader와Writer에게 공평하게 기회를 주는 경우First해결
▶스케쥴러가Writer에게 우선기회를 주는 경우Second해결
작가 프로세스( Writer Process )while (true){ wait(rw_mutex); // // Critical Section // Writing 코드란 // signal(rw_mutex); }。
wait(rw_mutex);:임계영역에Reader와Writer둘 다 없는 경우 (rw_mutex > 0)임계영역으로 진입
▶임계영역진입 시rw_mutex--로 인한rw_mutex == 0이므로 다른프로세스진입불가
。signal(rw_mutex):임계영역진입권한 반납
▶rw_mutex++
- Reader - Writer Locks
。Read또는Write의 동작 중 하나만 동작하도록 하는Locking방법
▶Readers - Writers Problem의 해결방법
。현재프로세스가Reader또는Writer의 구분을 한 후Reader Lock과Writer Lock을 각각 구현 및 획득
▶Reader Lock획득 시 여러프로세스들이임계영역에 접근 가능
▶Writer Lock획득 시 단일프로세스만임계영역에 접근가능
。JAVA예시
JAVA - Monitor로Reader-Writer Lock을 통한 작가-독자 문제 해결
출처
。운영체제 공룡책 강의