Pthread는 모든 유닉스 계열 POSIX 시스템에서, 일반적으로 이용되는 라이브러리로 병렬적으로 작동하는 소프트웨어 작성을 위해 사용할 수 있다. 소스코드에선 #include <pthread.h>로 헤더를 포함하고 컴파일 시 -pthread 옵션을 붙여 컴파일 한다.
주요 thread 함수
pthread_create
새로운 스레드를 생성하고 지정된 함수 start_routine을 실행한다.
int pthread_create(pthread_t *thread, const pthread_attr_t *attr, void *(*start_routine) (void *), void *arg);
- pthread_t *thread: 생성된 스레드의 식별자를 저장할 포인터, 해당 식별자를 통해 이후 스레드를 제어할 수 있다.
- const pthread_attr_t *attr: 스레드의 속성을 지정하는 포인터로 기본 속성을 사용하려면 NULL을 전달한다.
- void *(*start_routine) (void *): 스레드가 실행할 함수에 대한 포인터로 스레드가 실행할 함수는 해당 형식을 지켜야 한다.
- void *arg: 스레드 함수에 전달할 인자이다.
- 리턴값 : 성공 시 0을 반환하고, 실패 시 오류 코드를 반환한다.
pthread_join
지정된 스레드가 종료될 때까지 대기한다.
int pthread_join(pthread_t thread, void **retval);
- pthread_t thread: 대기할 스레드의 식별자
- void **retval: 스레드가 종료 시 반환하는 값을 저장할 포인터이다.
- 리턴값 : 성공 시 0을 반환하고, 실패 시 오류 코드를 반환한다.
pthread_detach
지정된 스레드를 분리(detach) 상태로 설정한다. 그렇게 분리된 스레드는 종료 될 때 그 자원을 자동으로 해제하며, 부모스레드에서는 pthread_join으로 스레드 종료를 기다리지 않고 detach한 이후 다른 작업을 수행할 수 있다.
int pthread_detach(pthread_t thread);
- pthread_t thread: 분리할 스레드의 식별자
- 리턴값 : 성공 시 0을 반환하고, 실패 시 오류 코드를 반환한다.
주요 mutex 함수
pthread_mutex_init
뮤텍스 객체를 초기화 하는 함수로 뮤텍스가 사용되기 전에 반드시 호출되어야 한다.
int pthread_mutex_init(pthread_mutex_t *mutex, const pthread_mutexattr_t *attr);
- pthread_mutex_t *mutex: 초기화할 뮤텍스 객체에 대한 포인터이다.
- const pthread_mutexattr_t *attr: 뮤텍스의 속성을 지정하는 포인터로 기본 속성을 사용하려면 NULL을 전달한다.
- 리턴값 : 성공 시 0을 반환하고, 실패 시 오류 코드를 반환한다.
pthread_mutex_destroy
뮤텍스 객체를 해제한다. 이 함수는 더이상 뮤텍스가 사용되지 않을 때 호출한다.
int pthread_mutex_destroy(pthread_mutex_t *mutex);
- pthread_mutex_t *mutex: 해제할 뮤텍스 객체에 대한 포인터
- 리턴값 : 성공 시 0을 반환하고, 실패 시 오류 코드를 반환한다.
pthread_mutex_lock
뮤텍스를 잠근다. 만약 뮤텍스가 이미 잠겨있다면 뮤텍스가 해제될 때까지 대기한다.
int pthread_mutex_destroy(pthread_mutex_t *mutex);
- pthread_mutex_t *mutex: 잠글 뮤텍스 객체에 대한 포인터
- 리턴값 : 성공 시 0을 반환하고, 실패 시 오류 코드를 반환한다.
pthread_mutex_unlock
잠긴 뮤텍스를 해제한다. 이 함수는 반드시 pthread_mutex_lock로 뮤텍스를 잠근 스레드에서 쌍으로 호출되어야 한다. pthread_mutex_lock와 pthread_mutex_unlock 사이가 임계구역이 된다.
int pthread_mutex_lock(pthread_mutex_t *mutex);
- pthread_mutex_t *mutex: 해제할 뮤텍스 객체에 대한 포인터
- 리턴값 : 성공 시 0을 반환하고, 실패 시 오류 코드를 반환한다.
사용 예시
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
// foo 구조체 정의
struct foo {
int f_count;
pthread_mutex_t f_lock;
/* ... more stuff here ... */
};
// foo 메모리 할당 함수
struct foo *foo_alloc(void) /* allocate the object */
{
struct foo *fp = NULL;
if((fp = malloc(sizeof(struct foo))) != NULL) {
fp->f_count = 0;
if(pthread_mutex_init(&fp->f_lock, NULL) != 0) {
free(fp);
return(NULL);
}
}
return(fp);
}
// foo 메모리 해제 함수
void foo_free(struct foo *fp)
{
pthread_mutex_destroy(&fp->f_lock);
free(fp);
}
// 스레드 함수
void *thread_func(void *arg)
{
struct foo *fp = (struct foo *)arg;
for(int i = 0; i < 1000000; i++) {
pthread_mutex_lock(&fp->f_lock);
// 임계 구역 시작
fp->f_count++;
// 임계 구역 끝
pthread_mutex_unlock(&fp->f_lock);
}
return ((void *)0);
}
int main(void)
{
pthread_t tid1, tid2;
struct foo *fp;
fp = foo_alloc();
if(fp == NULL) {
fprintf(stderr, "foo_alloc failed\n");
exit(1);
}
// 스레드1 생성
if(pthread_create(&tid1, NULL, thread_func, (void *)fp) != 0) {
fprintf(stderr, "Error creating thread 1\n");
exit(1);
}
// 스레드2 생성
if(pthread_create(&tid2, NULL, thread_func, (void *)fp) != 0) {
fprintf(stderr, "Error creating thread 2\n");
exit(1);
}
pthread_join(tid1, NULL);
pthread_join(tid2, NULL);
printf("Final count: %d\n", fp->f_count);
foo_free(fp);
return 0;
}
위 예시에서 pthread_mutex_lock과 pthread_mutex_unlock을 사용하여 임계 구역을 보호하면 f_count의 최종 값이 반복 횟수에 정확히 2배가 나오는데 pthread_mutex_lock과 pthread_mutex_unlock 부분을 주석처리하면 여러 스레드가 동시에 동일한 변수 f_count에 접근하고 수정하려고 시도하므로 데이터 일관성이 깨질 수가 있다.
코드조각 참조 : APUE
'프로그래밍 > C' 카테고리의 다른 글
inet_addr, inet_aton, inet_pton, inet_ntoa, inet_ntop (0) | 2024.12.17 |
---|---|
winmm API 사용하여 WAV 파일 재생하기 (0) | 2024.07.04 |
C] epoll 사용법 (0) | 2024.06.24 |
__DATE__ 에서 필요한 정보를 추출하여 원하는 포맷으로 출력하기 (0) | 2024.04.17 |
C언어 ] qsort (Quick Sort, 퀵정렬) 함수 사용법 (0) | 2024.01.30 |