Developer
[thead] multi-thread programming for thread-safe
rocksea
2014. 2. 6. 10:55
multi-thread programming for thread-safe.
multi-thread환경에서의 개발을 하게 될 경우 thread-safe에 대한 고민은 하지 않을 수 가 없다.
이유인 즉 슨 다수의 thread가 하나의 global variable이나 static variable에 값에 대한 접근 및 변경 떄문인데 이를 해결하기 위
해 보통 mutex-lock 을 사용하여 programming을 하게 된다. 이러한 경우 2개의 thread가 존재 할 경우 먼저 실행된 thread가
자원을 선점하게 되고 두번쨰 thread는 unlock시점까지 대기상태가 된다.
thread-safe의 예
#include <stdio.h> #include <unistd.h> #include <pthread.h> int ncount; // 쓰레드간 공유되는 자원 pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; // 쓰레드 초기화 // 쓰레드 함 수 1 void* do_loop(void *data) { int i; pthread_mutex_lock(&mutex); // 잠금을 생성한다. for (i = 0; i < 10; i++) { printf("loop1 : %d\n", ncount); ncount ++; sleep(1); } pthread_mutex_unlock(&mutex); // 잠금을 해제한다. } // 쓰레드 함수 2 void* do_loop2(void *data) { int i; // 잠금을 얻으려고 하지만 do_loop 에서 이미 잠금을 // 얻었음으로 잠금이 해제될때까지 기다린다. pthread_mutex_lock(&mutex); // 잠금을 생성한다. for (i = 0; i < 10; i++) { printf("loop2 : %d\n", ncount); ncount ++; sleep(1); } pthread_mutex_unlock(&mutex); // 잠금을 해제한다. } int main() { int thr_id; pthread_t p_thread[3]; int status; int a = 1; ncount = 0; thr_id = pthread_create(&p_thread[0], NULL, do_loop, (void *)&a); sleep(3); thr_id = pthread_create(&p_thread[1], NULL, do_loop2, (void *)&a); pthread_join(p_thread[0], (void *) &status); pthread_join(p_thread[1], (void *) &status); pthread_join(p_thread[2], (void *) &status); status = pthread_mutex_destroy(&mutex); return 0; }
결과값
loop1 : 0
loop1 : 1
loop1 : 2
loop1 : 3
loop1 : 4
loop1 : 5
loop1 : 6
loop1 : 7
loop1 : 8
loop1 : 9
loop2 : 10
loop2 : 11
loop2 : 12
loop2 : 13
loop2 : 14
loop2 : 15
loop2 : 16
loop2 : 17
loop2 : 18
loop2 : 19
위 프로그램을 실행하면 총 두번의 mutex lock이 발생하게 된다.
futex(0x7f98ef6d09d0, FUTEX_WAIT, 25674, NULL) = 0
futex(0x7f98eeecf9d0, FUTEX_WAIT, 25683, NULL) = 0
* futex란 spin lock 이라 불리며 kernel space가닌 user space의 메모리에 존재함으로 성능상의 이득을 볼 수 있다. ( linux kernel 2.6이상 부터 지원. )
thread-safe 하지 않을 경우의 예
#include <stdio.h> #include <unistd.h> #include <pthread.h> int ncount; // 쓰레드간 공유되는 자원 pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; // 쓰레드 초기화 // 쓰레드 함 수 1 void* do_loop(void *data) { int i; // pthread_mutex_lock(&mutex); // 잠금을 생성한다. for (i = 0; i < 10; i++) { printf("loop1 : %d\n", ncount); ncount ++; sleep(1); } // pthread_mutex_unlock(&mutex); // 잠금을 해제한다. } // 쓰레드 함 수 2 void* do_loop2(void *data) { int i; // pthread_mutex_lock(&mutex); // 잠금을 생성한다. for (i = 0; i < 10; i++) { printf("loop2 : %d\n", ncount); ncount ++; sleep(1); } // pthread_mutex_unlock(&mutex); // 잠금을 해제한다. } int main() { int thr_id; pthread_t p_thread[3]; int status; int a = 1; ncount = 0; thr_id = pthread_create(&p_thread[0], NULL, do_loop, (void *)&a); thr_id = pthread_create(&p_thread[1], NULL, do_loop2, (void *)&a); pthread_join(p_thread[0], (void *) &status); pthread_join(p_thread[1], (void *) &status); status = pthread_mutex_destroy(&mutex); return 0; }
결과값
loop2 : 0
loop1 : 1
loop2 : 2
loop1 : 2
loop2 : 4
loop1 : 4
loop2 : 6
loop1 : 6
loop2 : 8
loop1 : 8
loop2 : 10
loop1 : 10
loop2 : 12
loop1 : 12
loop1 : 14
loop2 : 14
loop1 : 16
loop2 : 17
loop1 : 18
loop2 : 18
* thread-safe의 경우 동기화된 값을 출력하지만 thread-safe하지 않은경우 동기화 되지 않은 결과 값이 출력됨. ( single core CPU 환경에선 동시에 돌일이 없기 떄문에 의미 없음. )
mutex lock을 사용하지 않게되면 thread-safe하지 않은 값을 return 받게 될 수 있기때문에 따라서 multi-thread 환경에서는
global 변수 사용을 지양하고 local 변수의 사용을 권장한다. 그래야 thread-safe에 대한 안정성 보장을 할 수 있기 때문이다.
mutex lock을 사용하여 동기화를 구현 할 수 있지만 mutex lock은 잘못사용하게 될 경우 독이 될수 있기 때문이다.
( dead lock 등의 위험요소 존재.)
.by rocksea