C언어에서 뮤텍스와 조건 변수의 활용법

C언어에서 다중 스레드 환경은 효율적인 프로그램 실행을 가능하게 하지만, 동시에 스레드 간 데이터 공유로 인한 동기화 문제가 발생할 수 있습니다. 이를 해결하기 위해 뮤텍스와 조건 변수를 사용하면 안전하고 효율적인 동기화 메커니즘을 구현할 수 있습니다. 본 기사에서는 뮤텍스와 조건 변수의 개념, 활용 방법, 그리고 실용적인 응용 사례를 통해 이를 이해하고 구현하는 방법을 알아봅니다.

목차

멀티스레드 프로그래밍에서 동기화의 중요성


멀티스레드 프로그래밍은 성능을 최적화하고 프로그램의 응답성을 개선하는 강력한 도구입니다. 그러나 스레드 간 데이터 공유 시 동기화가 제대로 이루어지지 않으면 다음과 같은 문제가 발생할 수 있습니다.

데이터 경쟁 상태


두 개 이상의 스레드가 동일한 데이터에 동시 접근하여 수정할 경우, 데이터 불일치와 예기치 않은 동작이 발생할 수 있습니다.

데드락


스레드가 서로를 기다리는 상태에 빠지면서 프로그램이 더 이상 진행되지 않는 상황을 초래합니다.

리소스 불균형


리소스 접근 순서가 제어되지 않으면 특정 스레드가 지나치게 많은 리소스를 차지하거나, 어떤 스레드는 필요한 리소스를 아예 얻지 못할 수 있습니다.

멀티스레드 환경에서는 이러한 문제를 방지하기 위해 스레드 간의 데이터 공유를 동기화하고 적절한 리소스 관리를 수행하는 것이 필수적입니다. 뮤텍스와 조건 변수는 이러한 동기화를 효과적으로 구현할 수 있는 핵심 도구입니다.

뮤텍스의 기본 개념

뮤텍스(Mutex, Mutual Exclusion)는 멀티스레드 환경에서 공유 자원의 동시 접근을 제어하기 위한 동기화 메커니즘입니다. 뮤텍스는 한 번에 하나의 스레드만 공유 자원에 접근할 수 있도록 보장합니다.

뮤텍스의 주요 특징

  • 상호 배제: 공유 자원에 대한 동시 접근을 방지합니다.
  • 잠금 및 해제: 뮤텍스를 잠금(lock) 상태로 설정하면 다른 스레드가 해당 자원에 접근할 수 없게 되고, 해제(unlock)하면 접근이 가능합니다.
  • 블로킹 방식: 뮤텍스가 잠겨 있으면 다른 스레드는 해당 뮤텍스가 해제될 때까지 대기합니다.

뮤텍스 사용 방법


C언어에서 pthread 라이브러리를 사용해 뮤텍스를 초기화하고 사용할 수 있습니다. 다음은 기본적인 사용법입니다.

#include <pthread.h>
#include <stdio.h>

pthread_mutex_t mutex; // 뮤텍스 선언

void* thread_function(void* arg) {
    pthread_mutex_lock(&mutex); // 뮤텍스 잠금
    printf("Thread %d is running\n", *(int*)arg);
    pthread_mutex_unlock(&mutex); // 뮤텍스 해제
    return NULL;
}

int main() {
    pthread_t threads[2];
    int thread_ids[2] = {1, 2};

    pthread_mutex_init(&mutex, NULL); // 뮤텍스 초기화

    // 스레드 생성
    for (int i = 0; i < 2; i++) {
        pthread_create(&threads[i], NULL, thread_function, &thread_ids[i]);
    }

    // 스레드 종료 대기
    for (int i = 0; i < 2; i++) {
        pthread_join(threads[i], NULL);
    }

    pthread_mutex_destroy(&mutex); // 뮤텍스 제거
    return 0;
}

뮤텍스의 주요 활용 상황

  • 공유 데이터 구조 보호 (예: 리스트, 큐, 해시 테이블 등)
  • 파일 또는 데이터베이스 접근 제어
  • 스레드 간 작업 순서 보장

뮤텍스는 동기화의 기본 도구로, 다른 동기화 메커니즘(예: 조건 변수)과 결합하여 더욱 복잡한 동기화 요구 사항을 처리할 수 있습니다.

조건 변수란 무엇인가

조건 변수(Condition Variable)는 스레드 간의 상호작용을 지원하는 동기화 도구로, 특정 조건이 충족될 때까지 스레드를 대기 상태로 두거나 대기 중인 스레드를 깨울 수 있는 메커니즘입니다. 조건 변수는 뮤텍스와 함께 사용되며, 특정 조건을 기반으로 스레드 간 통신을 가능하게 합니다.

조건 변수의 주요 특징

  • 스레드 대기 제어: 조건이 만족될 때까지 스레드를 효율적으로 대기 상태로 둘 수 있습니다.
  • 뮤텍스와 결합: 뮤텍스를 사용하여 데이터 일관성을 유지하며 조건 변수를 활용합니다.
  • 상호 작용: 여러 스레드가 조건 변수를 통해 협력적으로 작업을 진행할 수 있습니다.

조건 변수 사용의 필요성


단순히 뮤텍스만으로는 특정 조건에 따라 스레드 간 통신을 구현하기 어렵습니다. 조건 변수는 다음과 같은 상황에서 유용합니다:

  • 생산자-소비자 문제: 생산자가 데이터를 생산하고, 소비자가 이를 소비하는 과정에서 대기와 신호를 관리.
  • 자원 가용성 대기: 특정 자원이 준비될 때까지 스레드를 대기 상태로 유지.

조건 변수의 기본 사용법


조건 변수는 pthread_cond_t로 선언되며, 주요 함수는 다음과 같습니다:

  • pthread_cond_wait: 스레드를 대기 상태로 만듭니다.
  • pthread_cond_signal: 대기 중인 스레드 하나를 깨웁니다.
  • pthread_cond_broadcast: 대기 중인 모든 스레드를 깨웁니다.

예제 코드:

#include <pthread.h>
#include <stdio.h>

pthread_mutex_t mutex;
pthread_cond_t cond;
int shared_data = 0;

void* producer(void* arg) {
    pthread_mutex_lock(&mutex);
    shared_data = 1; // 데이터 생산
    printf("Producer: Data produced\n");
    pthread_cond_signal(&cond); // 조건 신호 전송
    pthread_mutex_unlock(&mutex);
    return NULL;
}

void* consumer(void* arg) {
    pthread_mutex_lock(&mutex);
    while (shared_data == 0) { // 조건 확인
        pthread_cond_wait(&cond, &mutex); // 조건 대기
    }
    printf("Consumer: Data consumed\n");
    pthread_mutex_unlock(&mutex);
    return NULL;
}

int main() {
    pthread_t producer_thread, consumer_thread;

    pthread_mutex_init(&mutex, NULL);
    pthread_cond_init(&cond, NULL);

    pthread_create(&consumer_thread, NULL, consumer, NULL);
    pthread_create(&producer_thread, NULL, producer, NULL);

    pthread_join(producer_thread, NULL);
    pthread_join(consumer_thread, NULL);

    pthread_mutex_destroy(&mutex);
    pthread_cond_destroy(&cond);
    return 0;
}

조건 변수의 주요 활용

  • 작업 큐에서 작업 대기 및 실행
  • 특정 이벤트 발생 대기
  • 스레드 간 데이터 전달

조건 변수는 뮤텍스와 함께 사용함으로써 복잡한 동기화 요구 사항을 충족하고, 멀티스레드 환경에서 효율적인 작업 분배를 지원합니다.

뮤텍스와 조건 변수의 결합

뮤텍스와 조건 변수는 각각 독립적으로도 유용하지만, 함께 사용할 때 멀티스레드 환경에서 강력한 동기화 도구로 작동합니다. 두 메커니즘을 결합하면 스레드 간 데이터를 보호하면서 조건 기반 통신을 효과적으로 구현할 수 있습니다.

뮤텍스와 조건 변수의 결합 동작 원리

  1. 뮤텍스 잠금: 공유 데이터에 접근하기 전, 뮤텍스를 잠급니다.
  2. 조건 확인: 조건 변수를 사용해 특정 조건이 충족되었는지 확인합니다.
  3. 조건 대기: 조건이 충족되지 않았다면 pthread_cond_wait를 호출하여 대기 상태로 진입합니다.
  • 이 과정에서 뮤텍스가 자동으로 해제되어 다른 스레드가 공유 데이터에 접근할 수 있도록 합니다.
  1. 조건 신호 수신: 조건이 충족되면 pthread_cond_signal 또는 pthread_cond_broadcast를 호출해 대기 중인 스레드를 깨웁니다.
  2. 뮤텍스 해제: 작업이 완료되면 뮤텍스를 해제합니다.

뮤텍스와 조건 변수를 함께 사용하는 예제

생산자-소비자 문제를 해결하기 위한 코드:

#include <pthread.h>
#include <stdio.h>

pthread_mutex_t mutex;
pthread_cond_t cond;
int buffer = 0; // 공유 자원
int is_filled = 0; // 버퍼 상태

void* producer(void* arg) {
    pthread_mutex_lock(&mutex);
    while (is_filled) {
        pthread_cond_wait(&cond, &mutex); // 소비자가 버퍼를 비울 때까지 대기
    }
    buffer = 1; // 데이터 생성
    is_filled = 1;
    printf("Producer: Produced data\n");
    pthread_cond_signal(&cond); // 소비자에게 신호
    pthread_mutex_unlock(&mutex);
    return NULL;
}

void* consumer(void* arg) {
    pthread_mutex_lock(&mutex);
    while (!is_filled) {
        pthread_cond_wait(&cond, &mutex); // 생산자가 데이터를 생성할 때까지 대기
    }
    buffer = 0; // 데이터 소비
    is_filled = 0;
    printf("Consumer: Consumed data\n");
    pthread_cond_signal(&cond); // 생산자에게 신호
    pthread_mutex_unlock(&mutex);
    return NULL;
}

int main() {
    pthread_t producer_thread, consumer_thread;

    pthread_mutex_init(&mutex, NULL);
    pthread_cond_init(&cond, NULL);

    pthread_create(&producer_thread, NULL, producer, NULL);
    pthread_create(&consumer_thread, NULL, consumer, NULL);

    pthread_join(producer_thread, NULL);
    pthread_join(consumer_thread, NULL);

    pthread_mutex_destroy(&mutex);
    pthread_cond_destroy(&cond);

    return 0;
}

뮤텍스와 조건 변수 결합의 장점

  • 데이터 무결성 보장: 뮤텍스를 사용하여 데이터의 동시 접근을 방지합니다.
  • 효율적인 대기: 조건 변수를 통해 불필요한 CPU 소비 없이 조건 충족을 기다립니다.
  • 동기화 간결화: 여러 스레드 간의 협력을 간단하고 명확하게 구현할 수 있습니다.

뮤텍스와 조건 변수를 결합하여 멀티스레드 환경에서 안전하고 효율적인 동기화 메커니즘을 설계할 수 있습니다.

`pthread_cond_t`와 `pthread_mutex_t` 초기화

뮤텍스(pthread_mutex_t)와 조건 변수(pthread_cond_t)를 사용하기 위해서는 초기화가 반드시 선행되어야 합니다. 이 초기화는 멀티스레드 환경에서 두 데이터 구조가 올바르게 동작하도록 설정하는 중요한 단계입니다.

뮤텍스 초기화


뮤텍스는 pthread_mutex_init 함수를 사용하여 명시적으로 초기화하거나, 정적 초기화(PTHREAD_MUTEX_INITIALIZER)를 통해 초기화할 수 있습니다.

  1. 명시적 초기화
pthread_mutex_t mutex;
pthread_mutex_init(&mutex, NULL); // 기본 속성으로 초기화
  1. 정적 초기화
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
  • 주의사항: pthread_mutex_destroy를 호출하여 뮤텍스를 제거하지 않으면 리소스 누수가 발생할 수 있습니다.

조건 변수 초기화


조건 변수도 pthread_cond_init 함수 또는 정적 초기화를 사용할 수 있습니다.

  1. 명시적 초기화
pthread_cond_t cond;
pthread_cond_init(&cond, NULL); // 기본 속성으로 초기화
  1. 정적 초기화
pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
  • 주의사항: 사용이 끝난 후 pthread_cond_destroy를 호출하여 리소스를 해제해야 합니다.

초기화와 소멸 코드 예제

#include <pthread.h>
#include <stdio.h>

pthread_mutex_t mutex;
pthread_cond_t cond;

void initialize() {
    // 뮤텍스와 조건 변수 초기화
    pthread_mutex_init(&mutex, NULL);
    pthread_cond_init(&cond, NULL);
    printf("Mutex and Condition Variable Initialized\n");
}

void destroy() {
    // 뮤텍스와 조건 변수 제거
    pthread_mutex_destroy(&mutex);
    pthread_cond_destroy(&cond);
    printf("Mutex and Condition Variable Destroyed\n");
}

int main() {
    initialize();

    // 프로그램 로직 (생략)

    destroy();
    return 0;
}

초기화 시 고려 사항

  • 속성 설정: pthread_mutexattr_t 또는 pthread_condattr_t를 사용해 특정 속성을 설정할 수 있습니다.
  • 예: 뮤텍스 속성에서 재귀적 뮤텍스 설정.
  • 정적 초기화와 명시적 초기화 선택: 간단한 프로그램에서는 정적 초기화가 더 적합하며, 복잡한 설정이 필요한 경우 명시적 초기화를 사용합니다.
  • 초기화 실패 처리: 초기화 함수는 반환값으로 성공 여부를 반환하므로 이를 반드시 확인해야 합니다.

초기화는 스레드 동기화 메커니즘의 첫 단계로, 적절한 설정을 통해 이후의 동기화 작업을 안정적으로 수행할 수 있습니다.

조건 변수의 주요 함수 사용법

조건 변수는 pthread 라이브러리에서 제공하는 여러 함수를 통해 스레드 간 동기화를 구현합니다. 각 함수는 특정 역할을 하며, 뮤텍스와 결합하여 동작합니다.

`pthread_cond_wait`

  • 역할: 조건이 충족될 때까지 현재 스레드를 대기 상태로 만듭니다.
  • 구문:
int pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex);
  • cond: 대기할 조건 변수.
  • mutex: 보호할 뮤텍스.
  • 특징:
  • 호출 시 자동으로 뮤텍스를 해제하고, 조건이 충족되면 뮤텍스를 다시 잠급니다.
  • 이로 인해 다른 스레드가 공유 자원에 접근할 수 있도록 허용합니다.

`pthread_cond_signal`

  • 역할: 조건 변수를 기다리는 대기 중인 스레드 하나를 깨웁니다.
  • 구문:
int pthread_cond_signal(pthread_cond_t *cond);
  • cond: 신호를 보낼 조건 변수.
  • 특징:
  • 다수의 스레드가 대기 중인 경우, 하나의 스레드만 깨어납니다.

`pthread_cond_broadcast`

  • 역할: 조건 변수를 기다리는 모든 대기 중인 스레드를 깨웁니다.
  • 구문:
int pthread_cond_broadcast(pthread_cond_t *cond);
  • cond: 신호를 보낼 조건 변수.
  • 특징:
  • 다중 스레드가 같은 조건을 기다리는 경우 유용합니다.

조건 변수 함수의 사용 예제

#include <pthread.h>
#include <stdio.h>

pthread_mutex_t mutex;
pthread_cond_t cond;
int shared_data = 0;

void* producer(void* arg) {
    pthread_mutex_lock(&mutex);
    shared_data = 1; // 데이터 생성
    printf("Producer: Data produced\n");
    pthread_cond_signal(&cond); // 조건 신호 전송
    pthread_mutex_unlock(&mutex);
    return NULL;
}

void* consumer(void* arg) {
    pthread_mutex_lock(&mutex);
    while (shared_data == 0) {
        printf("Consumer: Waiting for data...\n");
        pthread_cond_wait(&cond, &mutex); // 조건 대기
    }
    printf("Consumer: Data consumed\n");
    shared_data = 0;
    pthread_mutex_unlock(&mutex);
    return NULL;
}

int main() {
    pthread_t producer_thread, consumer_thread;

    pthread_mutex_init(&mutex, NULL);
    pthread_cond_init(&cond, NULL);

    pthread_create(&producer_thread, NULL, producer, NULL);
    pthread_create(&consumer_thread, NULL, consumer, NULL);

    pthread_join(producer_thread, NULL);
    pthread_join(consumer_thread, NULL);

    pthread_mutex_destroy(&mutex);
    pthread_cond_destroy(&cond);

    return 0;
}

함수 사용 시 주의점

  1. 뮤텍스와 함께 사용: 조건 변수 함수는 뮤텍스와 반드시 함께 사용해야 데이터의 무결성을 유지할 수 있습니다.
  2. 스푸리어스 웨이크업: pthread_cond_wait는 조건이 충족되지 않아도 깨어날 수 있으므로 반드시 조건을 다시 확인해야 합니다.
  3. 신호 순서: pthread_cond_signal 또는 pthread_cond_broadcast는 조건이 변경된 후 호출해야 스레드가 정확히 작동합니다.

조건 변수의 주요 함수를 적절히 활용하면 효율적이고 안정적인 멀티스레드 동기화를 구현할 수 있습니다.

뮤텍스와 조건 변수의 응용 예시

뮤텍스와 조건 변수는 멀티스레드 환경에서 복잡한 동기화 문제를 해결하는 데 유용합니다. 특히 생산자-소비자 문제와 같은 시나리오에서 데이터 일관성과 효율적인 작업 분배를 구현할 수 있습니다.

생산자-소비자 문제

생산자-소비자 문제는 멀티스레드 프로그래밍에서 자주 다루는 사례로, 생산자가 데이터를 생성하고 소비자가 이를 소비하는 과정에서 동기화가 필요합니다.

문제 정의

  • 생산자는 데이터를 생성하여 버퍼에 추가합니다.
  • 소비자는 버퍼에서 데이터를 가져가서 처리합니다.
  • 버퍼가 가득 차거나 비어 있으면 생산자와 소비자는 대기해야 합니다.

구현 예제

#include <pthread.h>
#include <stdio.h>
#include <unistd.h>

#define BUFFER_SIZE 5

pthread_mutex_t mutex;
pthread_cond_t cond_not_empty;
pthread_cond_t cond_not_full;

int buffer[BUFFER_SIZE];
int count = 0;

void* producer(void* arg) {
    for (int i = 0; i < 10; i++) {
        pthread_mutex_lock(&mutex);

        while (count == BUFFER_SIZE) { // 버퍼가 가득 찬 경우 대기
            pthread_cond_wait(&cond_not_full, &mutex);
        }

        buffer[count++] = i; // 데이터 추가
        printf("Producer: Produced item %d\n", i);

        pthread_cond_signal(&cond_not_empty); // 소비자에게 신호
        pthread_mutex_unlock(&mutex);

        sleep(1); // 생산 속도 제어
    }
    return NULL;
}

void* consumer(void* arg) {
    for (int i = 0; i < 10; i++) {
        pthread_mutex_lock(&mutex);

        while (count == 0) { // 버퍼가 비어 있는 경우 대기
            pthread_cond_wait(&cond_not_empty, &mutex);
        }

        int item = buffer[--count]; // 데이터 소비
        printf("Consumer: Consumed item %d\n", item);

        pthread_cond_signal(&cond_not_full); // 생산자에게 신호
        pthread_mutex_unlock(&mutex);

        sleep(2); // 소비 속도 제어
    }
    return NULL;
}

int main() {
    pthread_t producer_thread, consumer_thread;

    pthread_mutex_init(&mutex, NULL);
    pthread_cond_init(&cond_not_empty, NULL);
    pthread_cond_init(&cond_not_full, NULL);

    pthread_create(&producer_thread, NULL, producer, NULL);
    pthread_create(&consumer_thread, NULL, consumer, NULL);

    pthread_join(producer_thread, NULL);
    pthread_join(consumer_thread, NULL);

    pthread_mutex_destroy(&mutex);
    pthread_cond_destroy(&cond_not_empty);
    pthread_cond_destroy(&cond_not_full);

    return 0;
}

코드 설명

  1. 버퍼 관리: buffer 배열과 count 변수를 통해 버퍼 상태를 관리합니다.
  2. 조건 변수:
  • cond_not_empty: 버퍼가 비어 있지 않을 때 소비자를 깨웁니다.
  • cond_not_full: 버퍼에 여유가 생겼을 때 생산자를 깨웁니다.
  1. 뮤텍스 사용:
  • pthread_mutex_lockpthread_mutex_unlock을 통해 데이터 무결성을 유지합니다.
  1. 루프와 대기: while 루프를 사용해 조건을 반복적으로 확인하며, 스푸리어스 웨이크업에 대비합니다.

응용 사례

  • 작업 큐 관리: 작업 생성 스레드와 작업 처리 스레드 간 동기화.
  • 이벤트 기반 프로그래밍: 특정 이벤트가 발생했을 때 스레드 동작 제어.
  • 데이터 스트림 처리: 데이터 생성 및 소비의 병렬 처리.

뮤텍스와 조건 변수의 조합을 활용하면 다양한 멀티스레드 동기화 시나리오를 효율적으로 처리할 수 있습니다.

스레드 동기화 문제 해결 팁

멀티스레드 환경에서 동기화 문제는 프로그램의 안정성과 성능에 큰 영향을 미칩니다. 뮤텍스와 조건 변수를 올바르게 사용하는 것 외에도 데드락 방지, 경쟁 상태 해결, 효율적인 리소스 관리를 통해 동기화 문제를 예방할 수 있습니다.

1. 데드락 방지


데드락은 두 개 이상의 스레드가 서로를 기다리면서 작업이 중단되는 상황입니다. 이를 방지하기 위한 몇 가지 방법은 다음과 같습니다:

1.1. 뮤텍스 잠금 순서 고정

  • 여러 뮤텍스를 잠가야 하는 경우, 모든 스레드가 동일한 순서로 뮤텍스를 잠그도록 설계합니다.

1.2. 타임아웃 설정

  • pthread_mutex_timedlock을 사용하여 뮤텍스 대기에 타임아웃을 설정하고, 타임아웃이 초과되면 적절히 처리합니다.

1.3. 최소한의 잠금 사용

  • 공유 자원에 필요한 최소 시간만 뮤텍스를 잠가 다른 스레드의 대기를 줄입니다.

2. 경쟁 상태 해결


경쟁 상태는 두 개 이상의 스레드가 예기치 않은 순서로 데이터에 접근하여 문제가 발생하는 상황입니다.

2.1. 공유 자원의 철저한 보호

  • 모든 공유 데이터를 뮤텍스와 함께 사용하여 무결성을 유지합니다.

2.2. 뮤텍스와 조건 변수 결합

  • 조건 변수를 활용하여 데이터 상태를 모니터링하고, 조건 충족 여부를 기반으로 스레드 동작을 제어합니다.

3. 스레드 간 신호 효율화


불필요한 대기를 방지하고, 스레드 신호를 효율적으로 처리합니다.

3.1. 조건 신호 최적화

  • pthread_cond_signalpthread_cond_broadcast를 상황에 맞게 선택적으로 사용합니다.
  • 단일 스레드가 대기 중일 경우 pthread_cond_signal을 사용해 불필요한 깨움을 방지합니다.
  • 다수의 스레드가 같은 조건을 기다릴 경우 pthread_cond_broadcast를 사용합니다.

4. 디버깅과 로그 활용


스레드 동기화 문제를 추적하고 해결하기 위해 디버깅과 로깅을 적극 활용합니다.

4.1. 디버깅 도구

  • valgrind와 같은 도구를 사용하여 데드락, 경쟁 상태를 감지합니다.

4.2. 상태 로그 추가

  • 중요한 동기화 이벤트(뮤텍스 잠금, 조건 변수 대기 등)에 로그를 추가하여 실행 흐름을 추적합니다.

5. 병렬성과 동기화 간 균형


동기화를 과도하게 사용하면 병렬 처리가 제한될 수 있습니다.

5.1. 뮤텍스 잠금 최소화

  • 필요하지 않은 경우 뮤텍스 잠금을 제거하거나, 읽기-쓰기 락(pthread_rwlock_t)을 활용해 읽기 작업을 병렬 처리합니다.

5.2. 작업 분리

  • 공유 자원 접근이 필요 없는 작업을 분리하여 병렬성을 최대화합니다.

예제: 데드락 방지를 위한 타임아웃 사용

#include <pthread.h>
#include <stdio.h>
#include <time.h>

pthread_mutex_t mutex;

void* thread_function(void* arg) {
    struct timespec ts;
    clock_gettime(CLOCK_REALTIME, &ts);
    ts.tv_sec += 2; // 2초 타임아웃 설정

    if (pthread_mutex_timedlock(&mutex, &ts) == 0) {
        printf("Thread %d acquired the lock\n", *(int*)arg);
        pthread_mutex_unlock(&mutex);
    } else {
        printf("Thread %d timed out\n", *(int*)arg);
    }
    return NULL;
}

int main() {
    pthread_t threads[2];
    int thread_ids[2] = {1, 2};

    pthread_mutex_init(&mutex, NULL);

    pthread_create(&threads[0], NULL, thread_function, &thread_ids[0]);
    pthread_create(&threads[1], NULL, thread_function, &thread_ids[1]);

    pthread_join(threads[0], NULL);
    pthread_join(threads[1], NULL);

    pthread_mutex_destroy(&mutex);
    return 0;
}

결론


스레드 동기화 문제를 해결하려면 올바른 설계와 도구의 적절한 활용이 필수적입니다. 데드락과 경쟁 상태를 방지하고 효율적인 자원 관리를 통해 멀티스레드 환경에서 안전하고 안정적인 프로그램을 구현할 수 있습니다.

요약

본 기사에서는 C언어에서 뮤텍스와 조건 변수를 활용하여 멀티스레드 환경에서 동기화를 구현하는 방법을 다뤘습니다. 뮤텍스는 공유 자원의 동시 접근을 제어하고, 조건 변수는 스레드 간의 조건 기반 통신을 가능하게 합니다. 생산자-소비자 문제와 같은 응용 사례를 통해 이들 메커니즘의 활용법과 효과를 구체적으로 살펴보았습니다. 적절한 동기화 전략과 문제 해결 방안을 통해 안정적이고 효율적인 멀티스레드 프로그래밍을 구현할 수 있습니다.

목차