[C언어] 20강 쓰레드(1)

강지원·2025년 1월 27일

리눅스 기반 C언어

목록 보기
23/24

1. 쓰레드란?

정의: 쓰레드는 프로세스 내에서 실행되는 하나의 독립적인 실행 흐름이다. 한 프로세스 내에 여러 쓰레드가 생성되어 병렬적으로 실행되는 것처럼 보일 수 있다.

특징:

  • 프로세스 내의 메모리와 자원을 공유한다.
  • 독립적인 실행 흐름을 가지므로 병렬 처리가 가능하다.
  • CPU 스케줄링과 컨텍스트 스위칭을 통해 실행된다.

2. 운영체제와 쓰레드

운영체제의 역할

  • 운영체제는 쓰레드 실행을 관리한다. 이를 위해 스케줄러와 메모리 관리, 자원 보호 메커니즘을 제공한다.
  • 운영체제 없이 쓰레드를 구현하는 것은 불가능하다.
  • 임베디드 시스템에서는 간단한 작업만 처리하는 경우 운영체제 없이도 코드를 직접 실행할 수 있지만, 이 경우 쓰레드처럼 멀티태스킹을 구현하려면 타이머 인터럽트와 같은 하드웨어 기반 방식으로 유사하게 동작하도록 직접 구현해야 한다.

임베디드 시스템에서 동작

  • 플래시 메모리에 프로그램 코드가 저장되어 있다.
  • 부팅 시 부트코드 실행 → 마지막에 main() 함수로 점프.
  • main()의 while(1) 루프에서 주기적인 작업이 실행된다.
  • 운영체제를 사용하는 임베디드 시스템에서는 RTOS(Real-Time Operating System)가 쓰레드 관리와 스케줄링을 수행한다.

3. 프로세스와 쓰레드의 차이

구분프로세스쓰레드
정의실행 중인 프로그램의 독립된 인스턴스프로세스 내의 독립적인 실행 흐름
메모리고유의 메모리 공간 사용프로세스 내의 자원 및 메모리 공간 공유
독립성각 프로세스는 독립적이며 다른 프로세스에 영향을 미치지 않음쓰레드는 프로세스 내의 다른 쓰레드와 영향을 주고받음
속도생성/종료 시 오버헤드가 크고 느림생성/종료가 상대적으로 가볍고 빠름
예시계산기, 그림판과 같은 독립 애플리케이션파일 읽기, 네트워크 요청 등 세부 작업 분할

4. 동기화: Mutex (뮤텍스)

뮤텍스(Mutex)

문제 상황:

  • 여러 쓰레드가 동일한 자원(A 변수)을 동시에 접근하면 데이터 무결성 문제가 발생.
    ex) th1이 변수 A를 처리 중일 때, 컨텍스트 스위칭이 발생하여 th2가 동일한 A를 변경하면 th1에 다시 돌아왔을 때 값이 예기치 않게 변경될 수 있다.

해결 방법:

  • 뮤텍스(Mutex): 쓰레드 간 자원 접근을 동기화하여 순서를 제어.
  • th1이 A에 Lock을 걸면, 다른 쓰레드(th2)는 대기 상태가 됨.
  • th1이 작업을 끝낸 후 Unlock을 하면, 대기 중인 th2가 접근 가능.

Deadlock(교착 상태)

쓰레드가 Lock을 걸고 나서 Unlock을 호출하지 않거나 다른 자원의 Lock을 기다리다 서로 대기 상태에 빠질 경우 Deadlock이 걸린다.

해결책:
타임아웃 설정: 일정 시간이 지나면 Lock을 강제로 해제.
교착 상태 회피 알고리즘: 자원 요청 순서를 정하거나 우선순위를 설정.

5. 쓰레드 메모리 관리

메모리 누수 (Memory Leak):
쓰레드가 사용한 메모리를 제대로 반환하지 않으면 메모리 누수가 발생. 이는 시스템 성능 저하 및 실행 중단의 원인이 됨.

예방:
자원을 사용 후 반드시 해제 (free() 등).
자동화된 메모리 관리 도구(스마트 포인터 등)를 사용하는 것도 고려.

6. 쓰레드 간단한 예제

1) 쓰레드 1개 만들기

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

int a=0;
void * thread1(void * arg){     //함수형은 상관없음

        printf("arg : %d\n", (int)arg);

        while(1){

                printf("thread1 a[%d]\n",++a);
                sleep(2);
        }

        return NULL;
}

int main(){

        pthread_t s_thread;
        int b = 88;
        
        pthread_create(&s_thread,NULL,thread1,(void *)b);
        pthread_join(s_thread,NULL);
}  

실행결과

$ gcc thread.c -lpthread

pthread 라이브러리 참조해줘야 함.

join을 해야 쓰레드가 돌아가는 것을 main이 기다려준다.

2) 쓰레드 2개 사용, join 말고 while 사용

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

int a=0;
void * thread1(void * arg){     //함수형은 상관없음

        printf("arg : %d\n", (int)arg);

        while(1){

                printf("thread%d a[%d]\n",(int)arg,++a);
                sleep(2);
        }

        return NULL;
}

int main(){

        pthread_t s_thread[2];
        int id1 = 77;
        int id2 = 88;

        pthread_create(&s_thread[0],NULL,thread1,(void *)id1);
        pthread_create(&s_thread[1],NULL,thread1,(void *)id2);
        while(1){
       			printf("main loop");
                sleep(1);
        }
        //pthread_join(s_thread[0],NULL);
        //pthread_join(s_thread[1],NULL);
}

이런식으로 하면 쓰레드가 3개가 돌아가는 것이다.

3) 뮤텍스 사용

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

int a=0;
pthread_mutex_t mutex;

void * thread1(void * arg){    

        printf("arg : %d\n", (int)arg);
        while(1){
                pthread_mutex_lock(&mutex);      //lock
                printf("thread%d a[%d]\n",(int)arg,++a);
                pthread_mutex_unlock(&mutex);    //unlock
                sleep(2);
        }

        return NULL;
}

int main(){

        pthread_t s_thread[2];
        int id1 = 77;
        int id2 = 88;

        pthread_mutex_init(&mutex,NULL);  //NULL = fast

        pthread_create(&s_thread[0],NULL,thread1,(void *)id1);
        pthread_create(&s_thread[1],NULL,thread1,(void *)id2);

        while(1){

                printf("main loop\n");
                sleep(1);
        }
        //pthread_join(s_thread[0],NULL);
        //pthread_join(s_thread[1],NULL);
}

a가 동시에 출력되는 것을 막아줌. 크리티컬 영역을 해결함.

0개의 댓글