티스토리 뷰

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

댓글