<aside> 🥕
상황 1 : 프로세스 A가 공유 메모리 공간에 데이터를 쓰고, 프로세스 B가 해당 메모리 공간을 읽는 상황 상황 2 : 같은 프로세스의 자원을 공유하는 스레드 간 통신이 이뤄지는 상황
</aside>
동시다발적으로 실행되는 프로세스 혹은 스레드를 다룰 때는, 언제나 임계 구역을 동시에 실행하지 않도록 유의해야함. 프로세스 혹은 스레드가 동시에 임계 구역 코드를 실행하여 문제가 발생하는 상황을 **레이스 컨디션(race condition)**이라함. 레이스 컨디션 발생 시 자원 일관성이 손상될 수 있기에 2개 이상의 프로세스 혹은 스레드가 임계 영역에 진입하고자 한다면 둘 중 하나는 작업이 끝날 때까지 대기해야함(레이스 컨디션은 소스 코드 상에서 발생 가능한 문제 상황)
#include <stdio.h>
#include <pthread.h>
int shared_data = 0 // 공유 데이터
void* increment(void* arg)
{
int i;
for(i=0; i<100000; i++)
{
shared_data += 1;
}
return NULL;
}
void* decrement(void* arg)
{
int i;
for(i=0; i<100000; i++)
{
shared_data -= 1;
}
return NULL;
}
int main()
{
pthread_t thread1, thread2;
pthread_create(&thread1, NULL, increment, NULL);
pthread_create(&thread2, NULL, decrement, NULL);
pthread_join(thread1, NULL);
pthread_join(thread2, NULL);
printf("공유 변수 마지막 결과 : %d\\n", shared_data);
return 0;
}
실행마다 결과가 다르게 나옴을 알 수 있음 : 레이스 컨디션이 발생했다는 의미
레이스 컨디션을 방지하면서 임계 구역을 관리하려면, 프로세스와 스레드가 **동기화(synchronization)**되어야함. 동기화는 다음 2가지 조건을 준수하면서 실행하는 것을 의미 :
<aside> 🥕
즉, 동기화는 “실행 순어 제어를 위한 동기화”가 있고 “상호 배제를 위한 동기화”가 존재
</aside>
#include <stdio.h>
#include <pthread.h>
pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
int shared_resource = 0;
int resource_available = 0;
int turn = 0; // 소비자 차례 제어 변수
void* producer(void* arg)
{
pthread_mutex_lock(&lock);
shared_resource = 42; // 공유 자원 생성
resource_available = 1;
turn = 0; // 첫 소비자 차례
pthread_cond_broadcast(&cond); // 소비자에게 알림
pthread_mutex_unlock(&lock);
return NULL;
}
void* consumer(void* arg)
{
int id = *(int *)arg;
pthread_mutex_lock(&lock);
while (!resource_available || turn != id) {
pthread_cond_wait(&cond, &lock); // 자원이 준비될 때까지 대기
}
printf("Consumed: %d by consumer %d\\n", shared_resource, id); // 공유 자원 소비
turn++; // 다음 소비자에게 차례 넘기기
pthread_cond_broadcast(&cond); // 모든 대기 중인 소비자에게 알림
pthread_mutex_unlock(&lock);
return NULL;
}
int main()
{
pthread_t producer_thread;
pthread_t consumers[3];
int consumer_ids[3] = {0, 1, 2};
// 스레드 생성
for (int i = 0; i < 3; i++) {
pthread_create(&consumers[i], NULL, consumer, (void *)&consumer_ids[i]);
}
pthread_create(&producer_thread, NULL, producer, NULL);
// 스레드 종료 대기
pthread_join(producer_thread, NULL);
for (int i = 0; i < 3; i++) {
pthread_join(consumers[i], NULL);
}
return 0;
}
여러 스레드를 생성해 공유 자원에 접근하지만, 순서 제어를 통해 순서대로 접근하도록 만듦
#include <stdio.h>
#include <pthread.h>
pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;
int shared_counter = 0;
void* increment(void* arg)
{
for(int i=0; i<100000; i++)
{
pthread_mutex_lock(&lock); // 임계 구역 시작
shared_counter++;
pthread_mutex_unlock(&lock); // 임계 구역 종료
}
return NULL;
}
int main()
{
pthread_t t1, t2;
pthread_create(&t1, NULL, increment, NULL);
pthread_create(&t2, NULL, increment, NULL);
pthread_join(t1, NULL);
pthread_join(t2, NULL);
printf("Final counter value: %d\\n", shared_counter);
return 0;
}
원래라면 공유 자원에 동시 접근하므로 레이스 컨디션이 발생해 값이 일정하게 20000이 안 나왔지만, 상호 배제 동기화를 통해 20000이 출력되는 것을 보장하게 됨