일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | |||||
3 | 4 | 5 | 6 | 7 | 8 | 9 |
10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 | 18 | 19 | 20 | 21 | 22 | 23 |
24 | 25 | 26 | 27 | 28 | 29 | 30 |
- 게임 개발
- SJF
- 운영체제
- 메카님
- Security
- 유니티
- MLFQ
- MAC
- 배경 그림
- link layer
- DSP
- DP
- 게임개발
- protection
- unity
- OSI 7계층
- polymorphism
- information hiding
- STCF
- Waterfall
- Unity #Indie Game
- SDLC
- OWASP
- frequency-domain spectrum analysis
- FIFO
- 컴퓨터 네트워크
- AINCAA
- Trap
- stride
- 유스케이스
- Today
- Total
다양한 기록
조건 변수, 생산자/소비자 문제 본문
락은 상호배제를 위해서
조건변수는 동기화를 위해서
void *child(void *arg) {
printf("child\n");
return NULL;
}
int main(int argc, char *argv[]) {
printf("parent: begin\n");
pthread_t c;
pthread_create(&c, NULL, child, NULL);
printf("parent: end\n");
return 0;
}
부모 스레드와 자식 스레드 중 무엇이 먼저 끝날지 알 수 없음.
volatile int done = 0;
void *child(void *arg) {
printf("child\n");
done = 1;
return NULL;
}
int main(int argc, char *argv[]) {
printf("parent: begin\n");
pthread_t c;
pthread_create(&c, NULL, child, NULL);
while(done == 0)
;
printf("parent: end\n");
return 0;
}
1. 변수로 비지 웨이팅하는 방법
소프트웨어적으로 해결하는 방식, 자식이 하나면 작동은 함
문제: 스핀이라 낭비가 심하고(비효율적), 자식이 여러개면 부정확
int done = 0;
pthread_mutex_t m = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t c = PTHREAD_COND_INITIALIZER;
void thr_exit() {
pthread_mutex_lock(&m);
done = 1;
pthread_cond_signal(&c);
pthread_mutex_unlock(&m);
}
void *child(void *arg) {
printf("child\n");
thr_exit();
return NULL;
}
void thr_join() {
pthread_mutex_lock(&m);
while( done == 0 )
pthread_cond_wait(&c, &m);
pthread_mutex_unlock(&m);
}
int main(int argc, char *argv[]) {
printf("parent: begin\n");
pthread_t p;
pthread_create(&p, NULL, child, NULL);
thr_join();
printf("parent: end\n");
return 0;
}
조건 변수의 예시..
중요한 점: wait 앞 뒤로 락, 언락은 명시적으로 해줘야 하고, while 문으로 변수(done)를 확인해줘야 함
문제 상황 1) state variable (done) 안씀
void thr_exit() {
pthread_mutex_lock(&m);
pthread_cond_signal(&c);
pthread_mutex_unlock(&m);
}
void thr_join() {
pthread_mutex_lock(&m);
pthread_cond_wait(&c, &m);
pthread_mutex_unlock(&m);
}
경우 1: 부모가 먼저 실행되는 경우
부모는 기다리고, 자식이 끝난 다음에 다시 깨어나서 실행된다.
경우 2: 자식이 먼저 수행되는 경우
자식이 시그널을 보냈는데 부모는 애초에 wait에 들어가지 않았고,
자식이 끝난 다음에야 wait에 들어가서 깨어나지 않게 되는 경우가 발생
문제 상황 2)
void thr_exit() {
done = 1;
pthread_cond_signal(&c);
}
void thr_join() {
if( done == 0 )
pthread_cond_wait(&c);
}
1. 일단 락이 없어서 상호배제가 안됨
2. while문이 아니면 waiting 중이던 스레드가 다시 깨어났을 때 done이 조건에 맞는지 재확인이 안됨
생산자, 소비자 문제
생산자는 만들어서 버퍼에 넣어주고 소비자는 버퍼(크기 1)에서 꺼내는 상황을 가정
int buffer;
int count = 0;
void put(int value) {
assert(count == 0);
count = 1;
buffer = value;
}
int get() {
assert(count == 1);
count = 0;
return buffer;
}
void *producer(void *arg) {
int i;
int loops = (int)arg;
for(i = 0; i < loops; i++) {
put(i);
}
}
void *consumer(void *arg) {
while(1) {
int tmp = get();
printf("%d\n", tmp);
}
}
쉐어링을 고려하지 않은 코드
cond_t cond;
mutex_t mutex;
void *producer(void *arg) {
int i;
for( i = 0; i < loops; i++ ) {
pthread_mutex_lock(&lock);
if(count == 1)
pthread_cond_wait(&cond, &mutex);
put(i);
pthread_cond_signal(&cond);
pthread_mutex_unlock(&mutex);
}
}
void *consumer(void *arg) {
int i;
for( i = 0; i < loops; i++ ) {
pthread_mutex_lock(&mutex);
if(count == 0)
pthread_cond_wait(&cond, &mutex);
int tmp = get();
pthread_cond_signal(&cond);
pthread_mutex_unlock(&mutex);
printf("%d\n", tmp);
}
}
부정확한 코드
소비자가 여럿일 때, 이미 if 문을 지나 대기하고 있던 소비자가 다른 소비자에 의해 깨워지면
count에 대한 검사가 없이 실행됨 -> 버퍼가 비어있는데 가져가려고 할 가능성이 존재함
=> while 문으로 검사를 해줘야 함
cond_t cond;
mutex_t mutex;
void *producer(void *arg) {
int i;
for( i = 0; i < loops; i++ ) {
pthread_mutex_lock(&mutex);
while( count == 1 )
pthread_cond_wait(&cond, &mutex);
put(i);
pthread_cond_signal(&cond);
pthread_mutex_unlock(&mutex);
}
}
void *consumer(void *arg) {
int i;
for( i = 0; i < loops; i++ ) {
pthread_mutex_lock(&mutex);
while( count == 0 )
pthread_cond_wait(&cond, &mutex);
int tmp = get();
pthread_cond_signal(&cond);
pthread_mutex_unlock(&mutex);
printf("%d\n", tmp);
}
}
이래도 문제가 있음
생산자가 하나, 소비자가 여럿이라 할 때,
생산자가 슬립 중인데 소비자가 소비하고 다른 소비자를 깨우면 생산자에 시그널을 보낼 수가 없어진다.
cond_t empty, fill;
mutex_t mutex;
void *producer(void *arg) {
int i;
for( i = 0; i < loops; i++ ) {
pthread_mutex_lock(&mutex);
while( count == 1 )
pthread_cond_wait(&empty, &mutex);
put(i);
pthread_cond_signal(&fill);
pthread_mutex_unlock(&mutex);
}
}
void *consumer(void *arg) {
int i;
for( i = 0; i < loops; i++ ) {
pthread_mutex_lock(&mutex);
while( count == 0 )
pthread_cond_wait(&fill, &mutex);
int tmp = get();
pthread_cond_signal(&empty);
pthread_mutex_unlock(&mutex);
printf("%d\n", tmp);
}
}
조건 변수를 2개 두고, while 문에서 wait하도록 하여 해결된다.
int buffer[MAX];
int fill_ptr = 0;
int use_ptr = 0;
int count = 0;
void put(int value) {
buffer[fill_ptr] = value;
fill_ptr = (fill_ptr + 1) % MAX;
count++;
}
int get() {
int tmp = buffer[use_ptr];
use_ptr = (use_ptr + 1) % MAX;
count--;
return tmp;
}
cond_t empty, fill;
mutex_t mutex;
void *producer(void *arg) {
int i;
for( i = 0; i < loops; i++ ) {
pthread_mutex_lock(&mutex);
while( count == max )
pthread_cond_wait(&empty, &mutex);
put(i);
pthread_cond_signal(&fill);
pthread_mutex_unlock(&mutex);
}
}
void *consumer(void *arg) {
int i;
for( i = 0; i < loops; i++ ) {
pthread_mutex_lock(&mutex);
while( count == 0 )
pthread_cond_wait(&fill, &mutex);
int tmp = get();
pthread_cond_signal(&empty);
pthread_mutex_unlock(&mutex);
printf("%d\n", tmp);
}
}
버퍼의 크기가 늘어난 일반적인 버전
pthread_cond_broadcast()
pthread_cond_signal()은 기다리고 있던 스레드 하나만 깨움
pthread_cond_broadcast()는 조건 변수 해당하는 스레드 다 깨움
왜 이것이 필요한가
메모리가 충분하지 않은 상황(꽉 찼다고 가정)
T1: 100바이트 필요
T2: 50바이트 필요
free로 인해 발생한 시그널 -> 여유 공간이 50바이트 생겼다고 가정
pthread_cond_signal()을 쓴다고 할 때,
경우1: T2가 깨는 경우 -> T2 실행
경우2: T1이 깨는 경우
-> 여유공간이 부족해서 T1 슬립, T2는 깬 적이 없으니 실행 불가
=> pthread_cond_broadcast()로 일단 다 깨우면 T2가 실행이 가능
'운영체제' 카테고리의 다른 글
데드락 (0) | 2024.05.06 |
---|---|
Semaphore (세마포) (0) | 2024.04.18 |
Lock-based Concurrent Data Structure (0) | 2024.04.18 |
Lock의 구현 (0) | 2024.04.11 |
멀티 스레드 / 경쟁 상태, 공유자원, 임계영역, 상호배제 (0) | 2024.04.10 |