C 언어에서 pthread_cond를 활용한 조건 변수 사용법

C 언어로 멀티스레드 프로그래밍을 수행할 때, 스레드 간 동기화를 효율적으로 처리하기 위해 조건 변수(pthread_cond)가 자주 사용됩니다. 조건 변수는 특정 조건이 충족될 때까지 스레드가 기다리거나, 다른 스레드가 신호를 보내 작업을 재개하도록 제어하는 데 유용합니다. 본 기사에서는 조건 변수의 개념부터 사용 방법, 그리고 다양한 활용 사례를 통해 멀티스레드 프로그래밍의 핵심을 다룹니다. 이를 통해 효율적이고 안정적인 스레드 간 동기화를 구현하는 방법을 배우게 될 것입니다.

목차

멀티스레드와 조건 변수의 개념


멀티스레드 프로그래밍은 하나의 애플리케이션 내에서 여러 작업을 병렬로 처리할 수 있는 강력한 방법입니다. 하지만 스레드 간의 작업을 올바르게 동기화하지 않으면 데이터 충돌이나 경합 상태와 같은 문제가 발생할 수 있습니다.

조건 변수란 무엇인가?


조건 변수는 특정 조건이 충족될 때까지 스레드의 실행을 일시적으로 중단하고, 다른 스레드에서 해당 조건을 충족시키는 신호를 보낼 수 있도록 하는 동기화 메커니즘입니다.

멀티스레드에서 조건 변수가 필요한 이유

  • 효율적인 대기 상태 관리: 스레드가 폴링(반복 확인) 없이 조건이 충족될 때까지 효율적으로 기다릴 수 있습니다.
  • 작업 간 의존성 관리: 작업 순서를 제어하거나 데이터의 준비 상태를 기다리는 데 유용합니다.

조건 변수의 주요 구성 요소

  • 조건 변수(pthread_cond_t): 조건이 충족될 때 신호를 전달하는 역할을 합니다.
  • 뮤텍스(pthread_mutex_t): 조건 변수와 함께 사용되어 데이터 보호와 동기화를 보장합니다.

멀티스레드 환경에서 조건 변수는 복잡한 작업 의존성을 효과적으로 처리하는 데 필수적인 도구입니다.

pthread_cond의 초기화와 기본 사용법

조건 변수를 사용하려면 초기화부터 시작하여 올바르게 설정해야 합니다. pthread_cond는 POSIX 스레드 라이브러리에서 제공하는 데이터 타입으로, 조건 변수와 관련된 기능을 제공합니다.

조건 변수의 초기화


조건 변수는 두 가지 방법으로 초기화할 수 있습니다:

  1. 정적 초기화
pthread_cond_t cond = PTHREAD_COND_INITIALIZER;

이 방식은 조건 변수를 정적으로 초기화할 때 사용되며, 추가 설정 없이 바로 사용할 수 있습니다.

  1. 동적 초기화
pthread_cond_t cond;
pthread_cond_init(&cond, NULL);

pthread_cond_init 함수는 동적으로 조건 변수를 초기화하며, 두 번째 인자인 attr은 NULL로 설정하여 기본 속성을 사용합니다.

조건 변수의 제거


조건 변수를 더 이상 사용하지 않을 경우, 시스템 리소스를 해제하기 위해 pthread_cond_destroy를 호출해야 합니다.

pthread_cond_destroy(&cond);

기본 사용 절차


조건 변수는 일반적으로 다음 절차를 따릅니다:

  1. 조건 변수와 뮤텍스 선언 및 초기화
   pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
   pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
  1. 뮤텍스 잠금
    조건 변수를 사용하기 전에 뮤텍스를 잠가야 합니다.
   pthread_mutex_lock(&mutex);
  1. 조건 대기
    스레드는 특정 조건이 충족될 때까지 기다립니다.
   pthread_cond_wait(&cond, &mutex);
  1. 신호 전달 및 대기 해제
    다른 스레드에서 조건을 충족시키고 신호를 보냅니다.
   pthread_cond_signal(&cond);  // 단일 스레드에 신호
   pthread_cond_broadcast(&cond);  // 모든 대기 중인 스레드에 신호
  1. 뮤텍스 해제
    대기 작업이나 조건 처리가 완료되면 뮤텍스를 해제합니다.
   pthread_mutex_unlock(&mutex);

조건 변수는 정확한 초기화와 함께 사용해야 하며, 이를 통해 안전하고 효율적인 스레드 동기화를 구현할 수 있습니다.

pthread_cond_wait의 역할과 사용법

pthread_cond_wait는 조건 변수를 사용할 때 가장 중요한 함수 중 하나로, 스레드가 특정 조건이 충족될 때까지 기다리게 합니다. 이 함수는 뮤텍스와 함께 사용되며, 조건이 충족될 때까지 스레드가 효율적으로 대기 상태에 들어갈 수 있도록 지원합니다.

pthread_cond_wait의 역할

  • 스레드 대기: 특정 조건이 충족될 때까지 스레드를 일시 정지시킵니다.
  • 뮤텍스 자동 해제 및 재획득: 대기 중에 뮤텍스를 자동으로 해제하고, 조건이 충족되면 다시 획득하여 작업을 이어갑니다.

함수 정의

int pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex);
  • cond: 조건 변수의 포인터
  • mutex: 뮤텍스의 포인터

사용법

  1. 뮤텍스 잠금
    대기하기 전에 뮤텍스를 반드시 잠가야 합니다.
   pthread_mutex_lock(&mutex);
  1. 조건 대기
    조건이 충족될 때까지 pthread_cond_wait를 호출합니다.
   pthread_cond_wait(&cond, &mutex);
  • 이 과정에서 뮤텍스가 자동으로 해제됩니다.
  • 조건이 충족되면 뮤텍스를 다시 획득한 뒤 반환됩니다.
  1. 조건 충족 후 작업 수행
    조건이 충족되면 후속 작업을 수행합니다.
  2. 뮤텍스 해제
    작업이 완료되면 뮤텍스를 해제합니다.
   pthread_mutex_unlock(&mutex);

예제 코드


아래는 pthread_cond_wait를 사용하는 간단한 예제입니다.

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

pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
int ready = 0;

void* worker_thread(void* arg) {
    pthread_mutex_lock(&mutex);

    while (!ready) {
        printf("Worker thread: Waiting for condition...\n");
        pthread_cond_wait(&cond, &mutex);
    }

    printf("Worker thread: Condition met. Proceeding...\n");
    pthread_mutex_unlock(&mutex);
    return NULL;
}

void* signal_thread(void* arg) {
    pthread_mutex_lock(&mutex);
    ready = 1;
    printf("Signal thread: Sending signal...\n");
    pthread_cond_signal(&cond);
    pthread_mutex_unlock(&mutex);
    return NULL;
}

int main() {
    pthread_t t1, t2;
    pthread_create(&t1, NULL, worker_thread, NULL);
    pthread_create(&t2, NULL, signal_thread, NULL);

    pthread_join(t1, NULL);
    pthread_join(t2, NULL);

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

결론


pthread_cond_wait는 스레드 간 동기화에서 핵심적인 역할을 하며, 이를 적절히 활용하면 복잡한 작업 의존성을 효과적으로 관리할 수 있습니다. 그러나 올바른 뮤텍스 잠금 및 해제, 조건 충족 여부 확인 등의 작업을 신중히 처리해야 오류를 방지할 수 있습니다.

pthread_cond_signal 및 pthread_cond_broadcast

pthread_cond_signalpthread_cond_broadcast는 조건 변수를 사용하는 스레드 간 신호 전달을 처리하는 함수입니다. 이 두 함수는 대기 중인 스레드에 신호를 보내어 조건을 충족시키고 실행을 재개하도록 합니다.

pthread_cond_signal


pthread_cond_signal 함수는 대기 중인 스레드 중 하나에 신호를 보냅니다.

int pthread_cond_signal(pthread_cond_t *cond);
  • 역할: 조건 변수에 연결된 대기 중인 스레드 중 하나를 깨웁니다.
  • 사용 상황: 한 번에 하나의 스레드만 작업을 진행해야 할 때 적합합니다.

예제

pthread_mutex_lock(&mutex);
// 조건 충족 후 신호를 보냄
pthread_cond_signal(&cond);
pthread_mutex_unlock(&mutex);


위 코드는 뮤텍스를 잠근 상태에서 신호를 보내며, 스레드는 신호를 받은 후 실행을 재개합니다.

pthread_cond_broadcast


pthread_cond_broadcast 함수는 조건 변수에 연결된 모든 대기 중인 스레드에 신호를 보냅니다.

int pthread_cond_broadcast(pthread_cond_t *cond);
  • 역할: 대기 중인 모든 스레드를 깨웁니다.
  • 사용 상황: 여러 스레드가 동일한 조건에서 대기하고 있을 때, 한 번의 신호로 모두 실행을 재개해야 할 때 유용합니다.

예제

pthread_mutex_lock(&mutex);
// 조건 충족 후 모든 스레드에 신호를 보냄
pthread_cond_broadcast(&cond);
pthread_mutex_unlock(&mutex);


위 코드는 대기 중인 모든 스레드가 조건을 충족했음을 알리고, 각각 실행을 재개하도록 합니다.

signal과 broadcast의 차이점

함수역할사용 사례
pthread_cond_signal단일 스레드에 신호 전달생산자-소비자 모델에서 하나씩 처리
pthread_cond_broadcast모든 대기 중인 스레드에 신호 전달이벤트 완료 후 모두 실행 재개

사용 시 주의 사항

  1. 뮤텍스와 함께 사용: 조건 변수를 사용할 때 뮤텍스를 적절히 잠가야 합니다.
  2. 조건 확인 반복: 신호를 받은 후에도 조건을 반드시 다시 확인해야 합니다.
   while (!condition_met) {
       pthread_cond_wait(&cond, &mutex);
   }
  1. 효율적 사용: pthread_cond_signalpthread_cond_broadcast는 상황에 따라 적절히 선택해야 성능을 최적화할 수 있습니다.

결론


pthread_cond_signalpthread_cond_broadcast는 조건 변수와 함께 사용되는 중요한 함수로, 스레드 간 작업 조정을 돕습니다. 특정 조건 충족 시 단일 스레드와 다중 스레드에 신호를 보내는 적절한 메커니즘을 이해하고, 이를 활용하여 효율적인 멀티스레드 프로그램을 작성할 수 있습니다.

조건 변수와 mutex의 연계

조건 변수(pthread_cond_t)는 반드시 뮤텍스(pthread_mutex_t)와 함께 사용됩니다. 이는 조건 변수의 동작이 스레드 간의 동기화 및 데이터 보호를 보장하기 위해 뮤텍스와 밀접하게 연계되기 때문입니다.

조건 변수와 뮤텍스의 관계

  • 조건 변수: 스레드가 특정 조건이 충족될 때까지 대기하거나 신호를 받는 메커니즘을 제공합니다.
  • 뮤텍스: 조건 변수와 함께 사용되어, 대기 중인 데이터가 다른 스레드에 의해 변경되지 않도록 보호합니다.

뮤텍스와 조건 변수 사용의 주요 단계

  1. 뮤텍스 잠금
    조건 변수와 관련된 데이터에 접근하기 전에 뮤텍스를 잠가야 합니다.
   pthread_mutex_lock(&mutex);
  1. 조건 확인 및 대기
    조건이 충족되지 않으면 pthread_cond_wait를 호출하여 대기 상태에 들어갑니다. 이 과정에서 뮤텍스는 자동으로 해제됩니다.
   while (!condition_met) {
       pthread_cond_wait(&cond, &mutex);
   }
  • 조건이 충족되면 뮤텍스가 다시 잠기며 실행이 재개됩니다.
  1. 조건 충족 및 신호 전달
    조건이 충족되면 다른 스레드에서 pthread_cond_signal 또는 pthread_cond_broadcast를 호출하여 대기 중인 스레드에 신호를 보냅니다.
   pthread_cond_signal(&cond);
  1. 뮤텍스 해제
    작업이 완료되면 뮤텍스를 해제하여 다른 스레드가 데이터에 접근할 수 있도록 합니다.
   pthread_mutex_unlock(&mutex);

실제 예제


다음은 조건 변수와 뮤텍스를 연계하여 사용하는 간단한 예제입니다.

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

pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
int shared_data = 0;

void* producer(void* arg) {
    pthread_mutex_lock(&mutex);
    shared_data = 42;  // 조건 충족
    printf("Producer: Data produced, sending signal.\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 received: %d\n", shared_data);
    pthread_mutex_unlock(&mutex);
    return NULL;
}

int main() {
    pthread_t t1, t2;
    pthread_create(&t1, NULL, consumer, NULL);
    pthread_create(&t2, NULL, producer, NULL);

    pthread_join(t1, NULL);
    pthread_join(t2, NULL);

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

주의 사항

  1. 조건 확인 반복: pthread_cond_wait를 호출하기 전에 조건을 반드시 확인하고, 조건이 충족될 때까지 반복 확인해야 합니다.
  2. 뮤텍스 해제와 재획득: pthread_cond_wait는 대기 중 뮤텍스를 해제하고 신호를 받은 후 다시 획득하므로, 데이터의 일관성을 유지합니다.
  3. 데드락 방지: 뮤텍스를 적절히 관리하여 데드락 상태가 발생하지 않도록 주의해야 합니다.

결론


조건 변수와 뮤텍스의 연계는 스레드 간 동기화의 핵심입니다. 이 둘을 올바르게 사용하면, 멀티스레드 환경에서 안정적이고 효율적인 데이터 관리를 보장할 수 있습니다.

조건 변수 사용의 실제 예제

조건 변수는 멀티스레드 환경에서 효율적으로 작업을 동기화하는 데 필수적입니다. 아래에서는 조건 변수를 활용한 대표적인 동기화 패턴인 생산자-소비자 패턴을 통해 조건 변수의 실제 활용 방법을 설명합니다.

생산자-소비자 패턴


생산자-소비자 패턴은 한쪽에서 데이터를 생성(생산)하고, 다른 쪽에서 이를 처리(소비)하는 상황을 다룹니다. 이 과정에서 데이터 저장소가 가득 찼거나 비어 있는 상태를 조건 변수로 동기화합니다.

예제 코드


다음은 조건 변수와 뮤텍스를 사용한 생산자-소비자 패턴 구현 예제입니다.

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

#define BUFFER_SIZE 5
int buffer[BUFFER_SIZE];
int count = 0;

pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t cond_producer = PTHREAD_COND_INITIALIZER;
pthread_cond_t cond_consumer = PTHREAD_COND_INITIALIZER;

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

        while (count == BUFFER_SIZE) {  // 버퍼가 가득 찬 경우 대기
            printf("Producer: Buffer is full, waiting...\n");
            pthread_cond_wait(&cond_producer, &mutex);
        }

        buffer[count++] = i;
        printf("Producer: Produced item %d, buffer count: %d\n", i, count);

        pthread_cond_signal(&cond_consumer);  // 소비자에게 신호
        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) {  // 버퍼가 비어 있는 경우 대기
            printf("Consumer: Buffer is empty, waiting...\n");
            pthread_cond_wait(&cond_consumer, &mutex);
        }

        int item = buffer[--count];
        printf("Consumer: Consumed item %d, buffer count: %d\n", item, count);

        pthread_cond_signal(&cond_producer);  // 생산자에게 신호
        pthread_mutex_unlock(&mutex);
        sleep(2);
    }
    return NULL;
}

int main() {
    pthread_t producer_thread, consumer_thread;

    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_producer);
    pthread_cond_destroy(&cond_consumer);
    return 0;
}

코드 설명

  1. 버퍼와 카운트 변수
  • buffer: 생산된 데이터를 저장하는 배열.
  • count: 현재 버퍼에 저장된 데이터의 개수.
  1. 생산자(Producer)
  • 데이터를 생성하고 버퍼에 추가.
  • 버퍼가 가득 차면 pthread_cond_wait로 대기.
  • 소비자가 데이터를 처리하면 pthread_cond_signal로 알림.
  1. 소비자(Consumer)
  • 데이터를 소비하고 버퍼에서 제거.
  • 버퍼가 비어 있으면 pthread_cond_wait로 대기.
  • 생산자가 데이터를 추가하면 pthread_cond_signal로 알림.

출력 예시


프로그램 실행 시 예상되는 출력은 다음과 같습니다.

Producer: Produced item 0, buffer count: 1
Consumer: Consumed item 0, buffer count: 0
Producer: Produced item 1, buffer count: 1
Consumer: Consumed item 1, buffer count: 0
...

이점

  • 효율적인 대기: 조건 변수를 사용하여 필요할 때만 스레드가 실행됩니다.
  • 데이터 안전성: 뮤텍스를 사용해 버퍼 접근을 보호합니다.

결론


조건 변수와 뮤텍스를 조합하면 생산자-소비자와 같은 복잡한 동기화 문제를 효율적으로 해결할 수 있습니다. 이 예제를 기반으로 다른 동기화 문제에도 조건 변수를 응용할 수 있습니다.

조건 변수 사용 시 발생할 수 있는 문제

조건 변수는 멀티스레드 프로그래밍에서 효율적인 동기화를 제공하지만, 잘못된 사용은 다양한 문제를 초래할 수 있습니다. 이 섹션에서는 조건 변수 사용 시 발생할 수 있는 일반적인 문제와 이를 방지하거나 해결하는 방법을 설명합니다.

1. 신호 손실 문제


설명: 조건 변수를 기다리기 전에 신호(pthread_cond_signal 또는 pthread_cond_broadcast)가 발생하면, 해당 신호는 대기 중인 스레드가 없어 무시됩니다.

해결 방법: 신호 발생 전에 조건을 확인하고, 항상 while 루프를 사용하여 조건을 다시 확인합니다.

pthread_mutex_lock(&mutex);
while (!condition_met) {  // 반드시 while로 조건 확인
    pthread_cond_wait(&cond, &mutex);
}
pthread_mutex_unlock(&mutex);

2. 데드락(Deadlock)


설명: 뮤텍스 잠금을 해제하지 않아 스레드가 서로를 기다리는 상태가 될 수 있습니다.

해결 방법:

  • 항상 pthread_mutex_lockpthread_mutex_unlock이 쌍으로 호출되도록 보장합니다.
  • 코드 복잡성을 줄이고, 스레드 간 공유 리소스를 최소화합니다.

3. 스퍼리어스 웨이크업(Spurious Wakeup)


설명: 일부 시스템에서 조건이 충족되지 않았음에도 pthread_cond_wait이 반환될 수 있습니다.

해결 방법:

  • while 루프를 사용하여 조건을 반복적으로 확인합니다.
pthread_mutex_lock(&mutex);
while (!condition_met) {
    pthread_cond_wait(&cond, &mutex);
}
pthread_mutex_unlock(&mutex);

4. 우선순위 역전(Priority Inversion)


설명: 낮은 우선순위 스레드가 뮤텍스를 잠근 상태에서 높은 우선순위 스레드가 대기해야 하는 상황이 발생할 수 있습니다.

해결 방법:

  • 우선순위 상속(Priority Inheritance) 메커니즘을 활용하여 낮은 우선순위 스레드가 높은 우선순위로 실행될 수 있도록 합니다.
  • 필요 시 실시간 스케줄링 정책을 고려합니다.

5. 타임아웃 처리 누락


설명: 특정 조건이 충족되지 않을 경우, 대기 중인 스레드가 무한히 멈춰 있을 수 있습니다.

해결 방법:

  • pthread_cond_timedwait를 사용하여 대기에 타임아웃을 설정합니다.
struct timespec ts;
clock_gettime(CLOCK_REALTIME, &ts);
ts.tv_sec += 5;  // 5초 대기
if (pthread_cond_timedwait(&cond, &mutex, &ts) == ETIMEDOUT) {
    printf("Timeout occurred\n");
}

6. 조건 변수의 잘못된 해제


설명: 사용 중인 조건 변수를 pthread_cond_destroy로 제거하면 예기치 않은 동작이 발생할 수 있습니다.

해결 방법:

  • 조건 변수를 제거하기 전에 모든 스레드 작업이 종료되었는지 확인합니다.

문제 발생 방지의 베스트 프랙티스

  1. 조건 확인 반복: 항상 while 루프를 사용하여 조건을 확인합니다.
  2. 뮤텍스 관리 철저: 뮤텍스를 반드시 잠그고 해제하며, 논리적으로 일관되게 사용합니다.
  3. 디버깅 도구 활용: 멀티스레드 프로그램의 디버깅을 위해 valgrindgdb를 활용하여 문제를 분석합니다.
  4. 타임아웃 적용: 장시간 대기로 인해 프로그램이 멈추는 문제를 방지하기 위해 타임아웃을 설정합니다.

결론


조건 변수는 강력한 동기화 도구이지만, 올바르지 않은 사용은 데드락, 신호 손실 등의 문제를 초래할 수 있습니다. 신중하게 조건 확인과 뮤텍스 관리에 주의하고, 위의 해결 방법과 베스트 프랙티스를 적용하여 안정적인 프로그램을 작성해야 합니다.

고급 활용: 타임아웃 조건 변수

조건 변수를 사용할 때, 스레드가 무한히 대기 상태에 빠지지 않도록 타임아웃을 설정할 수 있습니다. pthread_cond_timedwait는 지정된 시간 동안만 조건 변수를 대기하도록 하여, 조건이 충족되지 않더라도 일정 시간이 지나면 대기에서 해제되도록 합니다.

pthread_cond_timedwait의 정의

int pthread_cond_timedwait(pthread_cond_t *cond, pthread_mutex_t *mutex, const struct timespec *abstime);
  • cond: 조건 변수의 포인터.
  • mutex: 보호할 뮤텍스의 포인터.
  • abstime: 절대 시간(UTC 시간 기준)을 나타내는 timespec 구조체.

타임아웃 설정 예제

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

pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
int condition_met = 0;

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

    pthread_mutex_lock(&mutex);

    while (!condition_met) {
        int ret = pthread_cond_timedwait(&cond, &mutex, &ts);
        if (ret == ETIMEDOUT) {
            printf("Thread: Timed out waiting for condition.\n");
            break;
        }
    }

    if (condition_met) {
        printf("Thread: Condition met, proceeding...\n");
    }

    pthread_mutex_unlock(&mutex);
    return NULL;
}

void* signal_condition(void* arg) {
    sleep(3);  // 3초 후 조건 충족
    pthread_mutex_lock(&mutex);
    condition_met = 1;
    printf("Signal thread: Sending signal...\n");
    pthread_cond_signal(&cond);
    pthread_mutex_unlock(&mutex);
    return NULL;
}

int main() {
    pthread_t t1, t2;
    pthread_create(&t1, NULL, wait_with_timeout, NULL);
    pthread_create(&t2, NULL, signal_condition, NULL);

    pthread_join(t1, NULL);
    pthread_join(t2, NULL);

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

코드 설명

  1. 타임아웃 설정
    clock_gettime을 사용하여 현재 시간을 가져오고, tv_sec 필드를 수정해 타임아웃 절대 시간을 설정합니다.
  2. 타임아웃 동작
  • pthread_cond_timedwait는 설정된 시간 동안 대기합니다.
  • 타임아웃이 발생하면 ETIMEDOUT을 반환합니다.
  1. 조건 충족 시 신호 처리
  • 조건이 충족되면 pthread_cond_signal로 대기 중인 스레드를 깨웁니다.
  • 타임아웃 전에 신호를 받은 경우, 스레드는 작업을 이어서 진행합니다.

출력 예시

Signal thread: Sending signal...
Thread: Condition met, proceeding...

또는, 신호가 늦게 도달할 경우:

Thread: Timed out waiting for condition.
Signal thread: Sending signal...

타임아웃 처리의 장점

  • 무한 대기 방지: 조건이 충족되지 않아도 일정 시간이 지나면 대기에서 해제됩니다.
  • 안정성 향상: 외부 의존성이 있는 작업에서 예기치 않은 지연으로 인한 스레드 정지가 방지됩니다.
  • 효율적 자원 관리: 대기 시간 초과 후 리소스를 재할당하거나 다른 작업을 수행할 수 있습니다.

주의 사항

  1. 시간 동기화 문제: 시스템 시간이 변경되면 abstime 설정이 영향을 받을 수 있습니다.
  2. 조건 확인 반복: 타임아웃이 발생해도 조건을 다시 확인해야 데이터의 일관성을 유지할 수 있습니다.

결론


pthread_cond_timedwait는 조건 변수를 고급스럽게 활용할 수 있는 도구로, 대기 시간이 지나치게 길어지는 상황을 방지합니다. 이를 통해 멀티스레드 프로그램의 안정성과 효율성을 크게 향상시킬 수 있습니다.

요약

본 기사에서는 C 언어의 pthread_cond를 활용한 조건 변수 사용법을 다뤘습니다. 조건 변수는 멀티스레드 프로그래밍에서 스레드 간 효율적인 동기화를 가능하게 하며, 뮤텍스와 함께 사용해 데이터 안전성과 동기화를 보장합니다.

특히, 조건 변수의 기본 개념, 초기화 및 사용법, 주요 함수(pthread_cond_wait, pthread_cond_signal, pthread_cond_broadcast)의 동작 방식, 타임아웃 처리 방법까지 구체적으로 설명했습니다. 또한, 생산자-소비자 패턴과 같은 실제 사례를 통해 조건 변수의 실용적인 활용법을 소개했습니다.

적절한 조건 변수 사용은 멀티스레드 프로그램의 성능과 안정성을 높이는 핵심 요소로, 이를 통해 효율적이고 신뢰성 높은 동기화를 구현할 수 있습니다.

목차