c 언어

C 멀티스레딩 이해하기

임베디드 친구 2024. 12. 15. 15:31
반응형

멀티스레딩은 동시에 여러 작업을 처리할 수 있도록 프로그램의 성능을 향상시키는 중요한 기술입니다. C에서는 pthread 라이브러리를 사용하여 멀티스레딩을 구현할 수 있습니다. 이번 포스팅에서는 멀티스레딩의 개념, C에서의 구현 방법, 그리고 몇 가지 예제를 통해 실습을 진행합니다.

1. 멀티스레딩이란?

멀티스레딩(Multithreading)은 하나의 프로세스 내에서 여러 실행 단위를 동시에 실행하는 기술입니다. 각 실행 단위를 스레드라고 하며, 멀티스레딩을 통해 프로그램은 다음과 같은 이점을 얻을 수 있습니다:

  • 성능 향상: 멀티코어 프로세서를 효율적으로 활용하여 병렬 처리 성능을 극대화합니다.
  • 응답성 개선: 사용자 인터페이스와 같은 작업이 블로킹되지 않도록 하여 응답성을 높입니다.
  • 리소스 공유: 동일한 메모리 공간을 공유하므로 프로세스 간 통신 오버헤드를 줄입니다.

하지만 스레드 간 공유 자원을 잘못 관리하면 데드락이나 레이스 컨디션과 같은 문제가 발생할 수 있으므로 주의가 필요합니다.

2. C에서의 멀티스레딩: pthread 라이브러리

C에서 멀티스레딩은 POSIX 스레드(POSIX Threads) 또는 pthread 라이브러리를 통해 구현됩니다. 이 라이브러리는 UNIX 계열 운영 체제에서 널리 사용되며, Windows에서도 일부 호환 라이브러리를 통해 사용할 수 있습니다.

2.1 주요 함수

pthread 라이브러리에서 자주 사용하는 함수는 다음과 같습니다:

  • pthread_create: 새로운 스레드를 생성합니다.
  • pthread_join: 스레드의 종료를 대기합니다.
  • pthread_exit: 스레드의 실행을 종료합니다.
  • pthread_mutex_*: 뮤텍스(Mutex)를 이용한 동기화 기능을 제공합니다.
  • pthread_cond_*: 조건 변수(Condition Variable)를 제공합니다.

3. 예제 코드

다음은 pthread를 사용한 간단한 멀티스레딩 예제입니다.

3.1 기본 스레드 생성

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

void* print_message(void* message) {
    printf("%s\n", (char*)message);
    return NULL;
}

int main() {
    pthread_t thread1, thread2;

    const char* message1 = "스레드 1: 안녕하세요!";
    const char* message2 = "스레드 2: 반갑습니다!";

    pthread_create(&thread1, NULL, print_message, (void*)message1);
    pthread_create(&thread2, NULL, print_message, (void*)message2);

    pthread_join(thread1, NULL);
    pthread_join(thread2, NULL);

    printf("메인 스레드 종료\n");
    return 0;
}

결과 출력

스레드 1: 안녕하세요!
스레드 2: 반갑습니다!
메인 스레드 종료

3.2 뮤텍스를 활용한 동기화

멀티스레딩에서는 공유 자원을 동시에 접근할 때 문제가 발생할 수 있습니다. 이를 방지하기 위해 뮤텍스를 사용합니다.

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

#define NUM_ITERATIONS 1000000

int counter = 0;
pthread_mutex_t lock;

void* increment_counter(void* arg) {
    for (int i = 0; i < NUM_ITERATIONS; i++) {
        pthread_mutex_lock(&lock);
        counter++;
        pthread_mutex_unlock(&lock);
    }
    return NULL;
}

int main() {
    pthread_t thread1, thread2;

    pthread_mutex_init(&lock, NULL);

    pthread_create(&thread1, NULL, increment_counter, NULL);
    pthread_create(&thread2, NULL, increment_counter, NULL);

    pthread_join(thread1, NULL);
    pthread_join(thread2, NULL);

    pthread_mutex_destroy(&lock);

    printf("최종 카운터 값: %d\n", counter);
    return 0;
}

결과 출력

최종 카운터 값: 2000000

3.3 조건 변수를 사용한 생산자-소비자 문제

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

#define BUFFER_SIZE 10

int buffer[BUFFER_SIZE];
int count = 0;
pthread_mutex_t lock;
pthread_cond_t not_empty;
pthread_cond_t not_full;

void* producer(void* arg) {
    for (int i = 0; i < 20; i++) {
        pthread_mutex_lock(&lock);
        while (count == BUFFER_SIZE) {
            pthread_cond_wait(&not_full, &lock);
        }
        buffer[count++] = i;
        printf("생산자: %d\n", i);
        pthread_cond_signal(&not_empty);
        pthread_mutex_unlock(&lock);
    }
    return NULL;
}

void* consumer(void* arg) {
    for (int i = 0; i < 20; i++) {
        pthread_mutex_lock(&lock);
        while (count == 0) {
            pthread_cond_wait(&not_empty, &lock);
        }
        int item = buffer[--count];
        printf("소비자: %d\n", item);
        pthread_cond_signal(&not_full);
        pthread_mutex_unlock(&lock);
    }
    return NULL;
}

int main() {
    pthread_t prod_thread, cons_thread;

    pthread_mutex_init(&lock, NULL);
    pthread_cond_init(&not_empty, NULL);
    pthread_cond_init(&not_full, NULL);

    pthread_create(&prod_thread, NULL, producer, NULL);
    pthread_create(&cons_thread, NULL, consumer, NULL);

    pthread_join(prod_thread, NULL);
    pthread_join(cons_thread, NULL);

    pthread_mutex_destroy(&lock);
    pthread_cond_destroy(&not_empty);
    pthread_cond_destroy(&not_full);

    return 0;
}

결과 출력

생산자: 0
소비자: 0
생산자: 1
소비자: 1
...

4. 멀티스레딩 구현 시 주의 사항

  1. 데드락: 뮤텍스 잠금을 잘못 관리하면 스레드가 서로 영원히 대기하는 상태가 발생할 수 있습니다.
  2. 경쟁 상태: 공유 자원에 대한 동시 접근을 적절히 관리하지 않으면 예기치 않은 동작이 발생할 수 있습니다.
  3. 리소스 관리: 생성한 스레드는 반드시 pthread_join으로 종료를 기다려야 메모리 누수를 방지할 수 있습니다.

5. 결론

C에서 멀티스레딩은 pthread 라이브러리를 통해 구현되며, 이를 통해 병렬 처리를 효율적으로 수행할 수 있습니다. 하지만 멀티스레딩은 동기화 문제를 포함한 다양한 복잡성을 동반하므로 정확한 이해와 주의가 필요합니다.

위 예제 코드를 실습하며 멀티스레딩의 기초를 익히고, 다양한 시나리오에서 멀티스레딩을 활용해 보세요!

반응형

'c 언어' 카테고리의 다른 글

C 디버깅과 최적화  (0) 2024.12.15
C와 하드웨어  (0) 2024.12.15
C 언어에서의 객체지향 프로그래밍  (0) 2024.12.15
C 언어의 표준 라이브러리  (0) 2024.12.14
C 언어의 전처리기  (0) 2024.12.14