Mutex vs Semaphore — 차이와 선택 기준
EmbeddedRTOSLinux
여러 스레드(또는 태스크)가 같은 자원에 접근할 때 동기화가 필요하다. Mutex와 Semaphore는 이 문제를 해결하는 두 가지 메커니즘인데, 목적이 다르다.
- Mutex: 공유 자원에 대한 배타적 접근(mutual exclusion)
- Semaphore: 이벤트 신호 전달 또는 리소스 카운팅
핵심 차이: 소유권
Mutex는 소유권이 있다. 잠금을 건 스레드만 해제할 수 있다.
Semaphore는 소유권이 없다. 한 스레드가 wait하고 다른 스레드가 post할 수 있다.
Mutex:
Thread A: lock → 자원 사용 → unlock (A만 unlock 가능)
Semaphore:
Thread A: wait (값 0이면 블로킹)
Thread B: post (A를 깨움) (누구든 post 가능)
이 차이에서 다른 모든 차이가 나온다.
| Mutex | Semaphore | |
|---|---|---|
| 목적 | 공유 자원 보호 | 신호 전달 / 리소스 카운팅 |
| 소유권 | 있음 (잠근 스레드만 해제) | 없음 |
| Priority Inheritance | 가능 | 불가 |
| 값 범위 | 0 또는 1 | 0 ~ N |
| ISR에서 사용 | 불가 | 가능 (post만) |
Mutex — 공유 자원 보호
두 스레드가 같은 버퍼에 쓴다. 동시에 쓰면 데이터가 깨진다.
pthread_mutex_t mtx = PTHREAD_MUTEX_INITIALIZER;
char shared_buf[256];
void* writer_a(void* arg) {
pthread_mutex_lock(&mtx);
snprintf(shared_buf, sizeof(shared_buf), "data from A");
pthread_mutex_unlock(&mtx);
return NULL;
}
void* writer_b(void* arg) {
pthread_mutex_lock(&mtx);
snprintf(shared_buf, sizeof(shared_buf), "data from B");
pthread_mutex_unlock(&mtx);
return NULL;
}
lock ~ unlock 사이(critical section)에는 한 스레드만 진입한다.
Semaphore — 이벤트 신호 전달
“데이터가 준비되었다”를 알리는 용도다. Producer-Consumer 패턴에서 쓴다.
sem_t data_ready;
void* producer(void* arg) {
prepare_data();
sem_post(&data_ready); // consumer에게 신호
return NULL;
}
void* consumer(void* arg) {
sem_wait(&data_ready); // 신호가 올 때까지 블로킹
process_data();
return NULL;
}
producer가 post하고, consumer가 wait한다. 서로 다른 스레드에서 post/wait를 호출하는 것이 Semaphore의 자연스러운 사용법이다.
Counting Semaphore — 리소스 풀
Semaphore의 값을 N으로 초기화하면, 최대 N개의 스레드가 동시에 접근할 수 있다.
#define POOL_SIZE 3
sem_t pool;
void init(void) {
sem_init(&pool, 0, POOL_SIZE); // 초기값 3
}
void* worker(void* arg) {
sem_wait(&pool); // 카운트 -1. 0이면 블로킹
use_connection(); // 최대 3개 스레드까지 동시 실행
sem_post(&pool); // 카운트 +1
return NULL;
}
DB 커넥션 풀, 제한된 하드웨어 채널 등 동시 접근 수를 제한할 때 사용한다.
선택 기준
**“누가 해제하는가”**로 판단한다.
- 잠금을 건 스레드가 직접 해제 → Mutex
- 다른 스레드가 신호를 보냄 → Semaphore
구체적으로:
| 상황 | 선택 |
|---|---|
| 전역 변수/버퍼를 여러 스레드가 읽고 씀 | Mutex |
| ISR에서 태스크에 이벤트 알림 | Binary Semaphore |
| Producer-Consumer 패턴 | Semaphore |
| 동시 접근 수 제한 (커넥션 풀) | Counting Semaphore |
| 우선순위가 다른 태스크 간 자원 공유 | Mutex (Priority Inheritance) |
RTOS 환경에서의 구체적인 API와 Priority Inversion 문제는 FreeRTOS Semaphore, Mutex, Priority Inversion에서 다룬다.
메모
- Binary Semaphore로 Mutex를 흉내낼 수 있지만 하면 안 된다. 소유권 추적이 없어서 Priority Inversion이 발생한다. Mars Pathfinder 로버가 이 실수로 반복 리셋된 사례가 있다
pthread_mutex_lock한 스레드가 아닌 다른 스레드에서pthread_mutex_unlock을 호출하면 undefined behavior다- Mutex를 여러 개 사용할 때는 항상 같은 순서로 획득해야 한다. 순서가 어긋나면 deadlock이 발생한다
sem_wait가EINTR로 리턴될 수 있다. 시그널 핸들러가 있는 환경에서는 리턴값을 확인해야 한다- Linux 커널에서는 POSIX API 대신
mutex_lock/mutex_unlock,down/up을 사용한다. 개념은 동일하다