http://www.joinc.co.kr/modules/moniwiki/wiki.php/Site/Thread/Beginning/PthreadApiReference
Docbook 원문
- 주의 : Ping Pong 예제 코드에 문제 있습니다. 데드락 걸릴 수 있습니다. 귀찮아서 수정하지 않고 있음...
Pthread API Reference
윤 상배
고친 과정 | ||
---|---|---|
고침 0.9 | 2004년 6월 30일 12시 | |
pthread 취소관련 api 추가 | ||
고침 0.8 | 2003년 10월 9일 12시 | |
pthread 시그널 관련 api 추가 |
- 차례
- 1. 소개
- 2. 기본 쓰레드 함수
-
- 2.1. pthread_create
- 2.2. pthread_join
- 2.3. pthread_detach
- 2.4. pthread_exit
- 2.5. pthread_cleanup_push
- 2.6. pthread_cleanup_pop
- 2.7. pthread_self
- 3. 쓰레드 동기화 함수
-
- 3.1. pthread_mutex_init
- 3.2. pthread_mutex_destroy
- 3.3. pthread_mutex_lock
- 3.4. pthread_mutex_unlock
- 3.5. pthread_cond_init
- 3.6. pthread_cond_signal
- 3.7. pthread_cond_boradcast
- 3.8. pthread_cond_wait
- 3.9. pthread_cond_timewait
- 3.10. pthread_cond_destroy
- 3.11. 예제코드
- 4. Thread Attribute 함수
-
- 4.1. pthread_attr_init
- 4.2. pthread_attr_distroy
- 4.3. pthread_attr_getscope
- 4.4. pthread_attr_setscope
- 4.5. pthread_attr_getdetachstate
- 4.6. pthread_attr_setdetachstate
- 5. 쓰레드 시그널 관련
-
- 5.1. pthread_sigmask
- 5.2. pthread_kill
- 5.3. sigwait
- 6. 쓰레드 취소
-
- 6.1. pthread_cancel
- 6.2. pthread_setcancelstate
- 6.3. pthread_setcancelstate
- 6.4. pthread_setcanceltype
- 6.5. pthread_testcancel
1. 소개
이 문서는 pthread 레퍼런스 문서이다. pthread 에서 제공하는 모든 함수의 레퍼런스를 제공하고 있지는 않지만, 자주 쓰일만한 대부분의 함수들은 정리되어 있음으로 참고할만한 가치가 있을것이다.
이 문서에 빠진 내용들은 계속 추가해 나갈 예정이다.
2. 기본 쓰레드 함수
주로 쓰레드 생성과 종료에 관련된 가장 기본적인 함수들이다.
2.1. pthread_create
int pthread_create(pthread_t *thread, const pthread_attr_t *attr, void *(*start_routine)(void *), void *arg); |
성공적으로 생성될경우 0을 리턴한다.
예제 : pthread_create.cc
#include <pthread.h> #include <stdio.h> #include <unistd.h> #include <stdlib.h> // 쓰레드 함수 void *t_function(void *data) { int id; int i = 0; id = *((int *)data); while(1) { printf("%d : %d\n", id, i); i++; sleep(1); } } int main() { pthread_t p_thread[2]; int thr_id; int status; int a = 1; int b = 2; // 쓰레드 생성 아규먼트로 1 을 넘긴다. thr_id = pthread_create(&p_thread[0], NULL, t_function, (void *)&a); if (thr_id < 0) { perror("thread create error : "); exit(0); } // 쓰레드 생성 아규먼트로 2 를 넘긴다. thr_id = pthread_create(&p_thread[1], NULL, t_function, (void *)&b); if (thr_id < 0) { perror("thread create error : "); exit(0); } // 쓰레드 종료를 기다린다. pthread_join(p_thread[0], (void **)&status); pthread_join(p_thread[1], (void **)&status); return 0; } |
2.2. pthread_join
#include <pthread.h> int pthread_join(pthread_t th, void **thread_return); |
pthread_join.c
#include <pthread.h> #include <stdio.h> #include <unistd.h> #include <stdlib.h> // 쓰레드 함수 // 1초를 기다린후 아규먼트^2 을 리턴한다. void *t_function(void *data) { int num = *((int *)data); printf("num %d\n", num); sleep(1); return (void *)(num*num); } int main() { pthread_t p_thread; int thr_id; int status; int a = 100; thr_id = pthread_create(&p_thread, NULL, t_function, (void *)&a); if (thr_id < 0) { perror("thread create error : "); exit(0); } // 쓰레드 식별자 p_thread 가 종료되길 기다렸다가 // 종료리턴값을 가져온다. pthread_join(p_thread, (void *)&status); printf("thread join : %d\n", status); return 0; } |
2.3. pthread_detach
int pthread_detach(pthread_t th); |
여기에서는 pthread_create 호출후 detach 하는 방법을 설명하고 있는데, pthread_create 호출시에 쓰레드가 detach 되도록 할수도 있다. 이에 대한 내용은 pthread_attr_setdetachstate 를 다루면서 설명하도록 하겠다.
예제 : pthread_detach.c
#include <pthread.h> #include <stdio.h> #include <unistd.h> #include <stdlib.h> // 쓰레드 함수 // 1초를 기다린후 아규먼트^2 을 리턴한다. void *t_function(void *data) { char a[100000]; int num = *((int *)data); printf("Thread Start\n"); sleep(5); printf("Thread end\n"); } int main() { pthread_t p_thread; int thr_id; int status; int a = 100; printf("Before Thread\n"); thr_id = pthread_create(&p_thread, NULL, t_function, (void *)&a); if (thr_id < 0) { perror("thread create error : "); exit(0); } // 식별번호 p_thread 를 가지는 쓰레드를 detach // 시켜준다. pthread_detach(p_thread); pause(); return 0; } |
[root@localhost test]# while [ 1 ]; do ps -aux | grep pthread | grep -v grep | grep -v vim; sleep 1; done root 2668 0.0 0.1 1436 292 pts/8 S 18:37 0:00 ./pthread_detach root 2668 0.0 0.1 1436 292 pts/8 S 18:37 0:00 ./pthread_detach |
2.4. pthread_exit
void pthread_exit(void *retval); |
예제 : pthread_exit.c
#include <pthread.h> #include <stdio.h> #include <unistd.h> #include <stdlib.h> // 쓰레드 함수 // 1초를 기다린후 아규먼트^2 을 리턴한다. void *t_function(void *data) { int num = *((int *)data); int i = 0; while(1) { if (i == 3) pthread_exit(0); printf("loop %d\n", i); i++; sleep(1); } } int main() { pthread_t p_thread; int thr_id; int status; int a = 100; thr_id = pthread_create(&p_thread, NULL, t_function, (void *)&a); if (thr_id < 0) { perror("thread create error : "); exit(0); } pthread_join(p_thread, (void **)&status); return 0; } |
2.5. pthread_cleanup_push
void pthrad_cleanup_push(void (*routine) (void *), void *arg); |
cleanup handlers 는 주로 자원을 되돌려주거나, mutex 잠금등의 해제를 위한 용도로 사용된다. 만약 mutex 영역에서 pthread_exit 가 호출되어 버릴경우 다른쓰레드에서 영원히 block 될수 있기 때문이다. 또한 malloc 으로 할당받은 메모리, 열린 파일지정자를 닫기 위해서도 사용한다.
예제 : pthread_cleanup.c
#include <pthread.h> #include <stdio.h> #include <unistd.h> #include <stdlib.h> #include <sys/types.h> // 쓰레드 함수 // 1초를 기다린후 아규먼트^2 을 리턴한다. // char *mydata; void cleanup(void *); void *t_function(void *data) { int num = *((int *)data); int i = 0; int a = 1; // cleanup handler 로 cleanup 함수를 // 지정한다. pthread_cleanup_push(cleanup, (void *)&a); mydata = (char *)malloc(1000); while(1) { if (i == 3) { // pthread_exit 가 호출되면서 // cleanup 을 호출하게 된다. pthread_exit(0); return 1; } printf("loop %d\n", i); i++; sleep(1); } pthread_cleanup_pop(0); } int main() { pthread_t p_thread; int thr_id; int status; int a = 100; thr_id = pthread_create(&p_thread, NULL, t_function, (void *)&a); if (thr_id < 0) { perror("thread create error : "); exit(0); } pthread_join(p_thread, (void **)&status); printf("Join finish\n"); } // cleanup handler void cleanup(void *myarg) { printf("thread is clean up\n"); printf("resource free\n"); free(mydata); } |
2.6. pthread_cleanup_pop
pthread_cleanup_push 와 함께 사용되며, install 된 cleanup handler 을 제거하기 위해서 사용된다.
void pthread_cleanup_pop(int execute); |
그리고 pthread_cleanup_push 와 pthread_cleanup_pop 은 반드시 같은 함수내의 같은 레벨의 블럭에서 한쌍으로 사용해야 한다.
2.7. pthread_self
pthread_t pthread_self(void); |
예제 : pthread_self.c
#include <pthread.h> #include <stdio.h> void *func(void *a) { pthread_t id; id = pthread_self(); printf("->%d\n", id); } int main(int argc, char **argv) { pthread_t p_thread; pthread_create(&p_thread, NULL, func, (void *)NULL); printf("%d\n", p_thread); pthread_create(&p_thread, NULL, func, (void *)NULL); printf("%d\n", p_thread); return 1; } |
3. 쓰레드 동기화 함수
쓰레드 동기화와 관련된 함수들이다.
3.1. pthread_mutex_init
int pthread_mutex_init(pthread_mutex_t * mutex, const pthread_mutex_attr *attr); |
pthread_mutex_init 는 mutex 객체를 초기화 시키기 위해서 사용한다. 첫번째 인자로 주어지는 mutex 객체 mutex를 초기화시키며, 두번째 인자인 attr 를 이용해서 mutex 특성을 변경할수 있다. 기본 mutex 특성을 이용하기 원한다면 NULL 을 사용하면 된다.
mutex 특성(종류) 에는 "fast", "recurisev", "error checking" 의 종류가 있으며, 기본으로 "fast" 가 사용된다.
// 뮤텍스 객체 선언 pthread_mutex_t mutex_lock; ... void *t_function() { pthread_mutex_lock(&mutex_lock); // critical section pthread_mutex_unlock(&mutex_lock); } int main() { pthread_t p_thread; int state; // 뮤텍스 객체 초기화, 기본 특성으로 초기화 했음 pthread_mutex_init(&mutex_lock, NULL); pthread_create(&p_thread, NULL, t_function, (void *)&a); ... pthread_join(&p_thread, (void **)&status); } |
3.2. pthread_mutex_destroy
int pthread_mutex_destroy(pthread_mutex_t *mutex); |
pthread_mutex_destroy 를 이용해서 제대로 mutex 를 삭제하려면 이 mutex 는 반드시 unlock 상태이여야 한다.
3.3. pthread_mutex_lock
int pthread_mutex_lock(pthread_mutex_t *mutex); |
만약 다른 어떤 쓰레드에서도 mutex lock 을 사용하고 있지 않다면, 즉시 mutex lock 을 얻을수 있게 되고 critcal section 에 진입하게 된다. critcal section 에서의 모든 작업을 마쳐서 사용하고 있는 mutex lock 이 더이상 필요 없다면 pthread_mutex_unlock 를 호출해서 mtuex lock 를 되돌려준다.
3.4. pthread_mutex_unlock
int pthread_mutex_unlock(pthread_mutex_t *mutex); |
3.5. pthread_cond_init
int pthread_cond_init(pthread_cond_t *cond, const pthread_cond_attr *attr); |
조건변수 cond는 상수 PTHREAD_COND_INITIALIZER 을 이용해서도 초기화 할수 있다. 즉 다음과 같은 2가지 초기화 방법이 존재한다.
pthread_cond_t cond = PTHREAD_COND_INITIALIZER; or pthread_cond_init(&cond, NULL); |
3.6. pthread_cond_signal
int pthread_cond_signal(pthread_cond_t *cond); |
3.7. pthread_cond_boradcast
int pthread_cond_broadcast(pthread_cond_t *cond); |
3.8. pthread_cond_wait
int pthread_cond_wait(pthread_cond_t cond, pthread_mutex_t *mutex); |
3.9. pthread_cond_timewait
int pthread_cond_timedwait(pthread_cont_t *cond, pthread_mutex_t *mutex, const struct timespec *abstime); |
또한 pthread_cond_timedwait함수는 다른 signal 에 의해서 interrupted 될수 있으며 이때 EINTR 을 리턴한다. 이 함수를 쓸때는 interrupted 상황에 대한 처리를 해주어야 한다.
3.10. pthread_cond_destroy
int pthread_cond_destroy(pthread_cond_t *cond); |
3.11. 예제코드
이번장에서 설명한 쓰레드 동기화 관련 함수의 이해를 돕기 위해서 간단한 예제를 준비했다. 설명은 주석으로 대신한다.
예제 : pthrad_sync_api.c
#include <pthread.h> #include <unistd.h> #include <stdio.h> #include <stdlib.h> #include <vector> #include <iostream> using namespace std; void *ping(void *); void *pong(void *); pthread_mutex_t sync_mutex; pthread_cond_t sync_cond; pthread_mutex_t gmutex = PTHREAD_MUTEX_INITIALIZER; pthread_cond_t gcond = PTHREAD_COND_INITIALIZER; int main() { vector<void *(*)(void *)> thread_list; vector<pthread_t> tident(10); int thresult; int status; int i; pthread_mutex_init(&sync_mutex, NULL); pthread_cond_init(&sync_cond, NULL); thread_list.push_back(pong); thread_list.push_back(ping); for(i = 0; i < thread_list.size(); i++ ) { pthread_mutex_lock(&sync_mutex); if (pthread_create(&tident[i], NULL, thread_list[i], (void *)NULL) <0) { perror("error:"); exit(0); } pthread_cond_wait(&sync_cond, &sync_mutex); pthread_mutex_unlock(&sync_mutex); } for (i = 0; i < tident.size(); i++) { pthread_join(tident[i], (void **)&status); } } void *ping(void *data) { int i=0; pthread_mutex_lock(&sync_mutex); pthread_cond_signal(&sync_cond); pthread_mutex_unlock(&sync_mutex); while(1) { pthread_mutex_lock(&gmutex); printf("%d : ping\n", i); pthread_cond_signal(&gcond); pthread_cond_wait(&gcond, &gmutex); pthread_mutex_unlock(&gmutex); usleep(random()%100); i++; } } void *pong(void *data) { int i = 0; pthread_mutex_lock(&sync_mutex); sleep(1); pthread_cond_signal(&sync_cond); pthread_mutex_unlock(&sync_mutex); while(1) { pthread_mutex_lock(&gmutex); pthread_cond_wait(&gcond, &gmutex); printf("%d : pong\n", i); pthread_cond_signal(&gcond); pthread_mutex_unlock(&gmutex); i++; } } |
위의 예제는 ping&pong 프로그램으로 ping 쓰레드와 pong 쓰레드가 각각 번갈아가면서 "ping", "pong" 을 날리는 프로그램이다. 2개의 영역에 걸쳐서 크리티컬섹션이 지정되어 있으며 각 크리티컬섹션안에는 쓰레드 동기화를 위해서 ptread_cond_signal 이 쓰여지고 있다.
위의 코드는 기본적으로 pong 쓰레드가 먼저 시그널을 대기하고 있다가 그 후 ping 쓰레드가 진입해서 "ping"을 날리고 시그널을 발생시키면 "pong" 메시지를 발생시키도록 되어 있다. 그렇다면 while 문에 있는 크리티컬 섹션에 반드시 pong 쓰레드가 먼저 진입할수 있도록 만들어줘야 할것이다. 그래서 위의 코드에서는 pong 쓰레드를 먼저 생성시켰다. 그러나 이것만으로는 충분하지 않다. 예를들어서 pong 쓰레드에서 크리티컬섹션에 들어가기 위해서 어떤 부가적인 작업이 있다고 했을때(메모리초기화, 기타 다른 함수 호출과 같은, 위에서는 sleep 으로 대신했다), 우리가 의도했던 바와는 다르게 ping 가 먼저 크리티컬섹션에 진입할수도 있다. 이럴경우 2개의 쓰레드는 교착상태에 빠지게 된다.
ping 쓰레드가 크리티컬섹션에 먼저 진입했을경우 ping 쓰레드는 "ping" 출력시키고 시그널을 발생시킬 것이고 pong 쓰레드가 "pong"를 출력시키고 시그널을 발생시킬때까지 시그널대기 하게 된다. ping 쓰레드가 시그널대기 하게 되면, 크리티컬섹션에 대한 뮤텍스 잠금이 해제됨으로 뒤늦게 크리티컬섹셔네 진입을 시도하던 pong 가 크리티컬섹션에 진입하고 ping 쓰레드에서부터 신호가 있는지 기다리게 될것이다. 그러나 ping 쓰레드는 이미 신호를 날려버렸음으로, pong 쓰레드는 결코 도착하지 않을 신호를 기다리며 영원히 시그널대기 하게 될것이다. 이런식으로 2개의 쓰레드는 교착상태에 빠져 버린다.
이 문제는 쓰레드간 동기화를 이용해서 해결할수 있으며, 위 코드에서는 mutex 잠금과, 조건변수를 이용해서 해결하고 있다. 물론 쓰레드간 동기화를 위해서 사용할수 있는 원시?적인 방법으로 sleep 나 usleep 같은 함수를 호출하는 방법도 있긴 하지만, ping 쓰레드에서 크리티컬 섹션에 진입하기전 1초 정도 sleep 을 주는 식으로 사용가능하지만 추천할만하진 않다. (간혹 간단하게 사용할수는 으며, 가장 확실한 방법을 제공해 주기도 한다)
4. Thread Attribute 함수
4.1. pthread_attr_init
int pthread_attr_init(pthread_attr_t *attr); |
성공할경우 0을 돌려주고 실패할경우 -1 을 되돌려준다.
4.2. pthread_attr_distroy
int pthread_attr_destroy(pthread_attr_t *attr); |
4.3. pthread_attr_getscope
int pthread_attr_getscope(const pthread_attr_t *attr, int *scope); |
#include <pthread.h> #include <stdlib.h> #include <stdio.h> int main() { pthread_attr_t pattr; int scope; pthread_attr_init(&pattr); pthread_attr_getscope(&pattr, &scope); if (scope == PTHREAD_SCOPE_SYSTEM) { printf("user mode thread\n"); } else if (scope == PTHREAD_SCOPE_PROCESS) { printf("Kernel mode thread\n"); } return 1; } |
4.4. pthread_attr_setscope
int pthread_attr_setscope(pthread_attr_t *attr, int scope); |
pthread_attr_setscope.c
#include <pthread.h> #include <stdlib.h> #include <stdio.h> int main() { pthread_attr_t pattr; int scope; pthread_attr_init(&pattr); pthread_attr_setscope(&pattr, PTHREAD_SCOPE_PROCESS); pthread_attr_getscope(&pattr, &scope); if (scope == PTHREAD_SCOPE_SYSTEM) { printf("user mode thread\n"); } else if (scope == PTHREAD_SCOPE_PROCESS) { printf("Kernel mode thread\n"); } return 1; } |
4.5. pthread_attr_getdetachstate
int pthread_attr_getdetachstate(pthread_attr_t *attr, int detachstate); |
기본은 PTHREAD_CREATE_JOINABLE 이며, pthread_detach를 이용해서 생성된 쓰레드를 detach 상태로 만들었을경우 또는 pthread_attr_setdetachstate함수를 이용해서 쓰레드를 detache 상태로 변경시켰을경우 PTHREAD_CREATE_DETACHED 상태가 된다.
예제 : pthread_attr_getdetachstate.c
#include <pthread.h> #include <stdlib.h> #include <stdio.h> pthread_attr_t attr; void *test(void *a) { int policy; printf("Thread Create\n"); pthread_attr_getdetachstate(&attr, &policy); if (policy == PTHREAD_CREATE_JOINABLE) { printf ("Join able\n"); } else if (policy == PTHREAD_CREATE_DETACHED) { printf ("Detache\n"); } } int main() { int status; pthread_t p_thread; pthread_attr_init(&attr); if (pthread_create(&p_thread, NULL, test, (void *)NULL) < 0) { exit(0); } pthread_join(p_thread, (void **)&status); } |
4.6. pthread_attr_setdetachstate
int pthread_attr_setdetachstate(pthread_attr_t *attr, int detachstate); |
pthread_attr_t attr; ... // JOINABLE 상태로 변경하고자 할때 pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE); // DETACHED 상태로 변경하고자 할때 pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); |
5. 쓰레드 시그널 관련
쓰레드간 프로세스와 쓰레드간 시그널 전달관련 API들이다. 자세한 내용은 쓰레드와 시그널을 참고하기 바란다.
5.1. pthread_sigmask
#include <pthread.h> #include <signal.h> int pthread_sigmask(int how, const sigset_t *newmask, sigset_t *oldmask); |
5.2. pthread_kill
#include <pthread.h> #include <signal.h> int pthread_kill(pthread_t thread, int signo); |
5.3. sigwait
#include <pthread.h> #include >signal.h> int sigwait(const sigset_t *set, int *sig); |
6. 쓰레드 취소
자세한 내용은 쓰레드 취소와 종료와 pthread_cancel(3)을 참고하기 바란다. 여기에서는 인덱스만 제공한다.
6.2. pthread_setcancelstate
#include <pthread.h> int pthread_setcancelstate(int state, int *oldstate); |
6.3. pthread_setcancelstate
#include <pthread.h> int pthread_setcancelstate(int state, int *oldstate); |