
<pthread.h>에 정의된 불투명(Opaque) 구조체들입니다. 직접 멤버 변수에 접근하지 말고 전용 함수를 써야 합니다.
| 자료형 | 역할 | 실무 팁 |
|---|---|---|
pthread_t | 쓰레드 식별자 (ID) | int가 아닐 수 있으므로 비교 시 == 대신 pthread_equal() 사용 권장. |
pthread_mutex_t | 뮤텍스 (잠금장치) | 공유 자원 보호용. LOCK -> CRITICAL SECTION -> UNLOCK |
pthread_cond_t | 조건 변수 | "신호(Signal)"를 기다릴 때 사용. 항상 뮤텍스와 짝으로 다님. |
pthread_attr_t | 속성 객체 | 스택 크기나 Detach 여부를 설정하고 create 할 때 넘겨줌. |
pthread_once_t | 1회 초기화 | 싱글톤 패턴이나 라이브러리 초기화에 사용 (PTHREAD_ONCE_INIT 필요). |
필수 헤더: #include <pthread.h> (sched.h는 스케줄링 정책 정의용)
| 함수 | 설명 | 비유 |
|---|---|---|
pthread_create | 쓰레드 생성 | 직원 고용. (성공 시 0 반환). |
pthread_join | 종료 대기 (Blocking) | 직원이 퇴근할 때까지 문 앞에서 기다림. (자원 회수 필수). |
pthread_detach | 독립 실행 (Non-blocking) | "알아서 하고 퇴근해." (종료 시 자동 자원 회수). join 불가. |
pthread_exit | 스스로 종료 | 직원이 "저 먼저 갑니다" 하고 나감. |
pthread_self | 내 ID 확인 | "내 사원증 번호가 뭐지?" |
뮤텍스란 동시에 한 스레드만 특정 코드 영역에 들어가게 만드는 공유 자원을 보호하기 위한 도구입니다.
코드 영역에 접근하는 것을 잠구거나 풀어서 공유 자원을 보호하는 역할을 합니다.
pthread_mutex_init / destroy: 생성 및 소멸pthread_mutex_lock / unlock: 잠그기 및 풀기pthread_attr_init)pthread_create의 두 번째 인자로 NULL을 주면 아래 기본값들이 적용됩니다.
| 속성 (Attribute) | 기본값 (Default) | 설명 |
|---|---|---|
| Detach State | PTHREAD_CREATE_JOINABLE | [중요] 기본적으로 join을 해줘야 메모리가 반환됨. |
| Scope | PTHREAD_SCOPE_SYSTEM | (리눅스 NPTL 기준) 커널이 1:1로 스케줄링함. |
| Inherit Sched | PTHREAD_INHERIT_SCHED | 부모(생성한 쓰레드)의 우선순위를 그대로 물려받음. |
| Sched Policy | SCHED_OTHER | 일반적인 시분할(Time-sharing) 스케줄링 (비실시간). |
| Stack Size | 시스템 기본값 (약 2MB~8MB) | 임베디드에서는 메모리 아끼려고 이 값을 줄여서(attr 설정) 생성함. |
참고: 리눅스(NPTL)는
PTHREAD_SCOPE_PROCESS를 지원하지 않습니다. (항상 커널 레벨 스케줄링).
#include <pthread.h>
#include <stdio.h>
#include <unistd.h>
// 쓰레드가 실행할 함수
void* worker_routine(void* arg) {
int id = *(int*)arg;
printf("[Thread] 일하는 중... (ID: %lu)\n", pthread_self());
return NULL;
}
int main() {
pthread_t thread;
int arg = 100;
// 1. 쓰레드 생성 (기본 속성 NULL 사용)
if (pthread_create(&thread, NULL, worker_routine, &arg) != 0) {
perror("Create failed");
return 1;
}
// 2. 메인 쓰레드는 기다림 (Joinable 상태이므로 필수)
pthread_join(thread, NULL);
printf("[Main] 쓰레드가 종료되었습니다.\n");
return 0;
}
Pthreads는 표준 라이브러리(libc)에 포함되지 않는 경우가 많아, 링크 옵션을 꼭! 줘야 합니다.
gcc -o my_app my_app.c -pthread
# 또는
gcc -o my_app my_app.c -lpthread
| 함수 | 설명 | 비유 |
|---|---|---|
pthread_join | 종료 대기 (Blocking) | 직원이 퇴근할 때까지 문 앞에서 기다림. (자원 회수 필수). |
pthread_join 함수는 목차 2번 에서 봤던 주요 api 핵심 함수 중 하나이다.
아래 예제를 통해 스레드의 종료 대기 및 자원 회수 메커니즘에 대해 알아보자.
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <pthread.h>
#include <string.h>
#include <stdint.h>
//#define _GNU_SOURCE /* See feature_test_macros(7) */
#include <unistd.h>
#include <sys/syscall.h>
void *thread_function(void *arg);
int main() {
int status;
pthread_t tid;
void *return_value;
int i;
status = pthread_create(&tid, NULL, thread_function, "hello thread\n");
if(status !=0){
perror("pthread_create");
exit(1);
}
for(i=1; i<=5; i++){
printf("Parent thread %d!!\n", i);
sleep(1);
}
//
status = pthread_join(tid, &return_value);
if(status != 0){
perror("pthread_join");
exit(1);
}
printf("Thread joined, it returned %s\n", (char *)return_value);
// printf("Thread joined, it returned %ld\n", (uintptr_t)return_value);
return 0;
}
void *thread_function(void *arg){
int i;
pid_t * tpid;
pthread_t * thread_id;
tpid=malloc(sizeof(pid_t));
thread_id=malloc(sizeof(pthread_t));
*tpid = syscall(SYS_gettid);
printf("Thread LWP: %d, Thread PID: %d\n", *tpid, getpid());
*thread_id = pthread_self();
printf("Thread ID: %lu\n", *thread_id);
for(i=1; i<=10; i++){
printf("\t\tChild thread %d\n", i);
sleep(1);
}
pthread_exit("Good Bye");
// return (void *)1;
//pthread_exit((void *)0);
}
마지막 출력 결과가 Thread joined, it returned (null)인 이유는 코드 마지막 줄 때문입니다.
// 실제 실행된 코드
pthread_exit((void *)0);
0은 주소로 치면 NULL입니다.printf("%s", ...)가 NULL 포인터를 받아서 (null)이라고 출력한 것입니다.pthread_exit("Good Bye");를 풀었다면, "Thread joined, it returned Good Bye"가 출력되었을 것입니다.pthread_join의 void (이중 포인터) 이해하기void *)"를 받아오고 싶을 때 사용한다.return_value)의 주소(&return_value)를 줘야, 함수가 그 안에 자식의 주소를 채워줄 수 있다.void *ptr; // 1. 자식의 보물을 가리킬 빈 지도
pthread_join(tid, &ptr); // 2. "이 지도에 보물 위치 좀 적어줘" (지도의 주소를 넘김)
// 3. 이제 ptr은 자식이 리턴한 값을 가리킴
pthread_exit() vs return쓰레드 함수(thread_function) 끝에서 두 방식은 동일하게 동작합니다.
return (void*)val;: C언어 문법. 함수가 끝나면서 값을 반환.pthread_exit((void*)val);: 쓰레드 전용 함수. 명시적으로 종료 알림.차이점: pthread_exit는 함수 깊은 곳(중첩된 함수)에서 호출해도 즉시 쓰레드를 종료시킬 수 있습니다.
joinable 쓰레드를 join 하지 않으면 좀비가 됩니다.
pthread_join()을 호출한다.pthread_detach()로 "결과 필요 없으니 알아서 사라져"라고 설정한다.thread_function 내부의 malloc은 free 되지 않았습니다. 아래처럼 자원을 정리하거나, join한 곳에서 free 할 수 있도록 리턴해줘야 합니다.
C
void *thread_function(void *arg){
// ... (생략) ...
// tpid, thread_id를 malloc 했지만 리턴하지 않고 함수가 끝남 -> Memory Leak
// 해결책 1: 다 썼으면 해제하기
free(tpid);
free(thread_id);
pthread_exit("Good Bye");
}
Thread 함수에 데이터를 넘기고 싶을땐 어떻게 해야하나요? 그리고 C++ boost thread와 posix thread의 차이가 있나요?