30장: Condition Variables
조건변수 개요
pthread_cond_t c; 선언 + 초기화 필요
wait와 signal을 통해 사용함
wait() 호출에 Mutex 변수가 필요함
pthread_cond_wait(pthread_cond_t* c, pthread_mutex_t *m);
조건 변수도 공유변수이기 때문에.
대기 상태로 갈 때 락을 해제함
깨어날 때 두 번째 인자 m에 대한 락을 다시 획득
조건변수 사용 기초
int done = 0;
pthread_mutex_t m = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t c = PTHRREAD_COND_INITIALIZER;
void thr_exit() {
pthread_mutex_lock(&m);
done = 1;
pthread_cond_signal(&c);
pthread_mutex_unlock(&m);
}
void thr_join() {
pthread_mutex_lock(&m);
while (done == 0)
pthread_cond_wait(&c, &m);
pthread_mutex_unlock(&m);
}
void *child(void* arg) {
printf("child");
thr_exit();
return NULL;
}
void main(int argc, char* argv[]) {
printf("parent");
pthread_t p;
pthread_create_t(&p, NULL, child, NULL);
thr_join();
printf("end");
return 0;
}
만약 Done이 없으면 ?
exit()에서 signal을 먼저 해버렸을 경우, join에서 계속 wait() 됨
부모가 못 깨어남
만약 mutex가 없으면 ?
void thr_exit() {
done = 1;
pthread_cond_signal(&c);
}
void thr_join() {
if (done == 0)
pthread_cond_wait(&c);
}
부모가 done==0에 의해 if문에 진입해서 wait 하려는데, 인터럽트 당함
자식이 done = 1;이랑 signal을 해버림
부모가 못 깨어남
생산자/소비자 문제
생산자 = 데이터 생산 후 버퍼에 둠
소비자 = 버퍼에서 데이터 가져다 씀
유한 버퍼 개념
정해진 크기의 버퍼에 생산자가 소비자가 연결되어 같이 작업함
공유 자원이므로 경쟁조건 발생 방지를 위해 동기화 필요
구현1: 조건 변수와 mutex 사용
cond_t cond;
mutex_t mutex;
void* producer(void* arg) {
for (int i=0; i<loop; i++) {
pthread_mutex_lock(&mutex); // p1
if (count == 1) // p2
pthread_cond_wait(&cond, &mutex); // p3
put(i); // p4
pthread_cond_signal(&cond); // p5
pthread_mutex_unlock(&mutex); // p6
}
}
void* consumer(void* arg) {
for (int i=0; i<loop; i++) {
pthread_mutex_lock(&mutex); // c1
if (count == 0) // c2
pthread_cond_wait(&cond, &mutex); // c3
int tmp = get(); // c4
pthread_cond_signal(&cond); // c5
pthread_mutex_unlock(&mutex); // c6
printf("%d", tmp);
}
}
생산자 1개, 소비자 1개라면 문제가 발생하지 않음
그러나, 같은 타입이 2개 이상이 된다면 문제가 발생
- if문으로 되어 있어서 Wait()를 했을 때, Mesa Semantics 문제 해결 X
- cond 변수가 1개라 누가 깨어날 지 모른다는 점
Tc1이 wait()에 갔으므로 Tp가 버퍼에 넣어주면 Tc1이 깨어나서 써야 함
그러나, Tc2가 가져가버림
Tc1은 if문으로 wait()를 했기 때문에,
깨어나서 다음 줄 c4로 가버리게 됨. (이때 버퍼는 비어 있으므로 에러)
→ while문으로 구현함
구현2: 상태 변수 점검에 while 도입
Mesa Semantics 문제를 while을 통해 버퍼를 계속 검사하게 하여 해결함
cond_t cond;
mutex_t mutex;
void* producer(void* arg) {
for (int i=0; i<loop; i++) {
pthread_mutex_lock(&mutex); // p1
while (count == 1) // p2
pthread_cond_wait(&cond, &mutex); // p3
put(i); // p4
pthread_cond_signal(&cond); // p5
pthread_mutex_unlock(&mutex); // p6
}
}
void* consumer(void* arg) {
for (int i=0; i<loop; i++) {
pthread_mutex_lock(&mutex); // c1
while (count == 0) // c2
pthread_cond_wait(&cond, &mutex); // c3
int tmp = get(); // c4
pthread_cond_signal(&cond); // c5
pthread_mutex_unlock(&mutex); // c6
printf("%d", tmp);
}
}
그러나 두 번째 문제가 발생함 (누가 깨어날 지 지정을 안 해줌)
- Tc2와 P가 wait() 상태일 때, Tc1이 버퍼 가져다 쓰고 signal을 보냄
- P가 깨어나야 하는데, Tc2가 깨어나버림
- Tc2는 버퍼가 비어 있으므로 c3에서 Wait()함
- 3개 모두 다 wait() 상태가 됨
구현3: 두 개의 조건 변수와 while 사용
cond_t empty, fill;
mutex_t mutex;
void* producer(void* arg) {
for (int i=0; i<loop; i++) {
pthread_mutex_lock(&mutex); // p1
while (count == 1) // p2
pthread_cond_wait(&empty, &mutex); // p3
put(i); // p4
pthread_cond_signal(&fill); // p5
pthread_mutex_unlock(&mutex); // p6
}
}
void* consumer(void* arg) {
for (int i=0; i<loop; i++) {
pthread_mutex_lock(&mutex); // c1
while (count == 0) // c2
pthread_cond_wait(&fill, &mutex); // c3
int tmp = get(); // c4
pthread_cond_signal(&empty); // c5
pthread_mutex_unlock(&mutex); // c6
printf("%d", tmp);
}
}
버퍼 추가해서 효율적인 동작
원형 큐 사용
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++;
}
void get() {
int tmp = buffer[use_ptr];
use_ptr = (use_ptr + 1) % MAX;
count--;
return tmp;
}
pthread_cond_t empty, fill;
pthread_mutex_t mutex;
void* producer(void* arg) {
for (int i=0; i<loop; 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) {
for (int i=0; i<loop; i++) {
pthread_mutex_lock(&mutex);
while (count == 0)
pthread_cond_wait(&fill, &mutex);
int tmp = get();
pthread_cond_signal(&empty);
pthread_mutex_unlock(&mutex);
}
}
Convering Condition (포함 조건)
멀티 스레드 환경에서 메모리 할당 생각
- 빈 공간이 없다고 가정
문제 상황
- A 스레드가 100의 메모리 할당 필요
- B 스레드가 10의 메모리 할당 필요
- 50의 여유 공간이 생겼다. 어떤 스레드르 깨울까 ?
→ signal() 대신, broadcast를 사용하여 적절한 스레드가 동작하게 함
void free(void* ptr, int size) {
pthread_mutex_lock(&m);
..
~~pthread_cond_signal(&c);~~ pthread_cond_broadcast(&c); 가 되어야 함
pthread_mutex_unlock(&m);
}
'🖥️ Computer Science > Operating System' 카테고리의 다른 글
[OS] 32장: Common Concurrency Problems (1) | 2024.07.05 |
---|---|
[OS] 31장: Semaphore (0) | 2024.07.04 |
[OS] 28장: Locks (0) | 2024.07.02 |
[OS] 19장: TLB (Translation Lookaside Buffers) (1) | 2024.07.01 |
[OS] 18장: 메모리 가상화의 Paging (0) | 2024.06.30 |