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 가능)

이 차이에서 다른 모든 차이가 나온다.

MutexSemaphore
목적공유 자원 보호신호 전달 / 리소스 카운팅
소유권있음 (잠근 스레드만 해제)없음
Priority Inheritance가능불가
값 범위0 또는 10 ~ 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;
}

producerpost하고, consumerwait한다. 서로 다른 스레드에서 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_waitEINTR로 리턴될 수 있다. 시그널 핸들러가 있는 환경에서는 리턴값을 확인해야 한다
  • Linux 커널에서는 POSIX API 대신 mutex_lock/mutex_unlock, down/up을 사용한다. 개념은 동일하다