병행 프로그래밍에서 동시에 여러 프로세스나 스레드가 접근할 때 발생할 수 있는 경합 상태(race condition)을 방지하는 방법을 알아보도록 하겠습니다
Mutual Exclusuin (상호 배제)
Progress (진행)
Bounded Waiting (한정 대기)
이 세 가지 조건을 충족시키기 위해, Mutex(뮤텍스)와 Semaphore(세마포어)와 같은 동기화 도구를 사용하여 구현할 수 있습니다. 이제 이 두 개념과 구현 예시를 통해, 코드에서 Race Condition을 어떻게 방지하는지 자세히 알아보겠습니다.
뮤텍스는 한 번에 하나의 스레드만이 자원에 접근할 수 있도록 보장합니다. C언어에서 'pthread_mutex_t' 타입을 사용하여 이를 구현합니다.
뮤텍스 객체를 생성하고 초기화합니다. 기본 속성 또는 사용자 정의 속성으로 초기화할 수 있습니다.
int pthread_mutex_init(pthread_mutex_t mutex, const pthread_mutexattr_t attr)
파라미터 정보:
예시:
pthread_mutex_t lock;
pthread_mutex_init(&lock, NULL);
뮤텍스 객체를 파괴하고 관련된 리소스를 해제합니다. 초기화된 뮤텍스를 해제하여 자원을 반환합니다.
int pthread_mutex_destroy(pthread_mutex_t *mutex)
파라미터 정보:
예시:
pthread_mutex_destroy(&lock)
해당 뮤텍스를 잠금으로써 다른 스레드가 임계 구역에 접근하지 못하게 합니다. 뮤텍스가 이미 잠겨있으면 스레드는 블록됩니다.
int pthread_mutex_lock(pthread_mutex_t *mutex)
파라미터 정보:
예시:
pthread_mutex_lock(&lock);
잠겨 있던 뮤텍스를 해제하여 다른 스레드가 임계 구역에 접근할 수 있게 합니다.
int pthread_mutex_unlock(pthread_mutex_t *mutex)
파라미터 정보:
예시:
pthread_mutex_unlock(&lock);
#include <pthread.h>
#include <stdio.h>
pthread_mutex_t lock;
void* thread_func(void* arg);
int main() {
pthread_t t1, t2;
int id1 = 1, id2 = 2;
// 뮤텍스 초기화
pthread_mutex_init(&lock, NULL);
// 두 개의 스레드 생성
pthread_create(&t1, NULL, thread_func, &id1);
pthread_create(&t2, NULL, thread_func, &id2);
// 두 스레드가 종료될 때까지 기다림
pthread_join(t1, NULL);
pthread_join(t2, NULL);
pthread_mutex_destroy(&lock);
return 0;
}
void* thread_func(void* arg) {
// 뮤텍스를 잠금
pthread_mutex_lock(&lock);
// 임계 구역 진입
printf("Thread %d in critical section\n", *(int*)arg);
// 뮤텍스 잠금 해제
pthread_mutex_unlock(&lock);
return NULL;
}
1. pthread_mutex_init(&lock, NULL):
lock이라는 뮤텍스를 초기화
2. pthread_create(&t1, NULL, thread_func, &id1):
첫 번째 스레드를 생성하여 thread_func을 실행하고, 스레드 식별자를 통해 스레드에 id1 값을 전달
3. pthread_create(&t2, NULL, thread_func, &id2):
두 번째 스레드를 생성하여 thread_func을 실행 후 스레드 식별자를 통해 스레드에 id2 값을 전달
4. pthread_join(t1, NULL):
첫 번째 스레드가 종료될 때까지 대기
5. pthread_join(t2, NULL):
두 번째 스레드가 종료될 때까지 대기
6. pthread_mutex_destroy(&lock):
lock 뮤텍스를 소멸시킴
thread_func 함수는 뮤텍스를 잠금으로 설정한 후, 임계 구역에 진입하여 "Thread X in critical section"을 출력하고, 뮤텍스 잠금을 해제합니다. 이로 인해 두 스레드는 동시에 임계 구역에 진입하지 못하고, 하나씩 차례로 임계 구역에 진입합니다.
세마포어는 자원에 대한 접근을 신호화(signaling)하고 제어하기 위해 사용됩니다. 이름 있는 세마포어와 이름 없는 세마포어의 두 가지 종류가 있습니다.
지정된 이름을 가진 세마포어를 생성하거나 열어서 세마포어 객체에 대한 포인터를 반환합니다. 필요한 경우 초기 값을 설정합니다.
sem_t sem_open(const char name, int oflag)
파라미터 정보:
예시:
sem_t *sem = sem_open("/example_sem", O_CREAT, 0644, 1);
세마포어 객체를 닫고, 프로그램이 더 이상 해당 세마포어를 사용하지 않음을 나타냅니다.
int sem_close(sem_t *sem)
파라미터 정보:
예시:
sem_close(sem);
세마포어 이름과 관련된 시스템 자원을 해제합니다. 세마포어가 더 이상 사용되지 않도록 합니다.
int sem_unlink(const char *name)
파라미터 정보:
예시:
sem_unlink("/example_sem");
세마포어 객체를 생성하고 초기 값으로 초기화합니다. 프로세스 간 또는 스레드 간 공유 여부를 설정합니다.
int sem_init(sem_t *sem, int pshared, unsigned int value)
파라미터 정보:
예시:
sem_t sem;
sem_init(&sem, 0, 1);
세마포어 객체를 파괴하고 관련된 리소스를 해제합니다.
int sem_destroy(sem_t *sem)
파라미터 정보:
예시:
sem_destroy(&sem);
세마포어 값을 감소시키며, 값이 0 이하이면 스레드는 블록됩니다. 자원이 사용 가능해질 때까지 대기합니다.
int sem_wait(sem_t *sem)
파라미터 정보:
예시:
sem_wait(&sem);
세마포어 값을 증가시켜 대기 중인 스레드 중 하나를 깨워서 자원에 접근할 수 있게 합니다.
int sem_post(sem_t *sem)
파라미터 정보:
예시:
sem_post(&sem);
#include <semaphore.h>
#include <pthread.h>
#include <stdio.h>
sem_t sem;
void* thread_func(void* arg);
int main() {
pthread_t t1, t2;
int id1 = 1, id2 = 2;
// 세마포어 초기화 (초기값 1)
sem_init(&sem, 0, 1);
// 두 개의 스레드 생성
pthread_create(&t1, NULL, thread_func, &id1);
pthread_create(&t2, NULL, thread_func, &id2);
// 두 스레드가 종료될 때까지 기다림
pthread_join(t1, NULL);
pthread_join(t2, NULL);
sem_destroy(&sem);
return 0;
}
void* thread_func(void* arg) {
// 세마포어 대기
sem_wait(&sem);
// 임계 구역 진입
printf("Thread %d in critical section\n", *(int*)arg);
// 세마포어 해제
sem_post(&sem);
return NULL;
}
1. sem_init(&sem, 0, 1):
sem이라는 세마포어를 초기화, 초기값은 1
2. pthread_create(&t1, NULL, thread_func, &id1):
첫 번째 스레드를 생성하여 thread_func을 실행. 스레드 식별자를 통해 스레드에 id1 값을 전달
3. pthread_create(&t2, NULL, thread_func, &id2):
두 번째 스레드를 생성하여 thread_func을 실행. 스레드 식별자를 통해 스레드에 id2 값을 전달
4. pthread_join(t1, NULL):
첫 번째 스레드가 종료될 때까지 대기
5. pthread_join(t2, NULL):
두 번째 스레드가 종료될 때까지 대기
6. sem_destroy(&sem):
sem 세마포어를 소멸시킴
thread_func 함수는 세마포어를 대기(sem_wait) 상태로 설정한 후, 임계 구역에 진입하여 "Thread X in critical section"을 출력하고, 세마포어를 해제(sem_post)합니다. 이로 인해 두 스레드는 동시에 임계 구역에 진입하지 못하고, 하나씩 차례로 임계 구역에 진입합니다.
Mutex를 사용할 때:
Semaphore를 사용할 때: