2012년 3월 20일 화요일

Multi-thread 환경에서의 메모리 공유

스레드를 사용하는 프로그램을 작성하던 초기에는, 하나의 스레드 이상을
사용할 일이 없었으므로, 문제가 없었다 (문제가 있더라도 어렵지 않았다).

최근들어 멀티 스레드로 프로그램을 작성하면서 생각하지 못했던 문제와
마주하게 되었다. 바로 스레드 동기화에 관련된 부분이다.

음?! 수업시간에 들었던 기억은 나는데...실제 개발시에 사용해본 일이
없다보니, 기억도 가물가물하고...언제 써야할지 떠오르지도 않고...아래의
코드는 뮤텍스를 통한 두 개의 스레드간 동기화에 대한 예제이다.

#include <stdio.h>
#include <process.h>
#include <windows.h>
//#include <pthread.h>  //유닉스/리눅스 계열에서 뮤텍스를 위한 헤더

#define MUTEX

int cnt = 0;  //각 스레드에서 접근하는 전역변수
#ifdef MUTEX
HANDLE hMutex;  //뮤텍스의 핸들러
//pthread_mutex_t hMutex;  //유닉스/리눅스 계열에서의 뮤텍스 핸들러
#endif

DWORD WINAPI Thread1(void *arg)
{
    int i, tmp;
    for(i=0; i<1000; i++)
    {
#ifdef MUTEX
        WaitForSingleObject(hMutex, INFINITE);  //뮤텍스에 대한 Lock
        //pthread_mutex_lock(hMutex)  //유닉스/리눅스 계열에서의 Lock
#endif
        tmp = cnt;
        Sleep(1000);
        cnt = tmp + 1;
        printf("[1]: %d\n", cnt);
#ifdef MUTEX
        ReleaseMutex(hMutex);  //뮤텍스에 대한 Unlock
        //pthread_mutex_unlock(hMutex)  //유닉스/리눅스 계열에서의 Unlock
#endif
    }
    printf("Thread1 End\n");
    return 0;
}

DWORD WINAPI Thread2(void *arg)
{
    int i, tmp;
    for(i=0; i<1000; i++)
    {
#ifdef MUTEX
        WaitForSingleObject(hMutex, INFINITE);  //뮤텍스에 대한 Lock
        //pthread_mutex_lock(hMutex)  //유닉스/리눅스 계열에서의 Lock
#endif
        tmp = cnt;
        Sleep(1000);
        cnt = tmp + 1;
        printf("[2]: %d\n", cnt);
#ifdef MUTEX
        ReleaseMutex(hMutex);  //뮤텍스에 대한 Unlock
        //pthread_mutex_unlock(hMutex)  //유닉스/리눅스 계열에서의 Unlock
#endif
    }
    printf("Thread2 End\n");
    return 0;
}

int main(int argc, char *argv[])
{
    HANDLE hThread[2];

#ifdef MUTEX
    hMutex = CreateMutex(NULL, FALSE, NULL);  //뮤텍스 생성 및 초기화
    //pthread_mutex_t hMutex = PTHREAD_MUTEX_INITIALIZER;  //유닉스/리눅스 계열에서의 뮤텍스 생성 및 초기화
    //pthread_mutex_t hMutex = pthread_mutex_init();  //유닉스/리눅스 계열에서의 뮤텍스 초기화(동적 초기화)
#endif

    hThread[0]=CreateThread(NULL, 0, Thread1, NULL, 0, NULL);
    hThread[1]=CreateThread(NULL, 0, Thread2, NULL, 0, NULL);

    WaitForMultipleObjects(2, hThread, TRUE, INFINITE);  //두 개의 스레드가 종료될때까지 대기

    printf("%d\n", cnt);  //결과값 출력

    CloseHandle(hThread[0]);
    CloseHandle(hThread[1]);
#ifdef MUTEX
    CloseHandle(hMutex);  //뮤텍스 핸들러 파괴
    //pthread_mutex_destroy(hMutex);  //유닉스/리눅스 계열에서의 뮤텍스 핸들러 파괴
#endif

    return 0;
}

코드를 실행해보면, #define MUTEX를 활성화 시킨 경우, 2000이 출력되며, 비활성화 시킨 경우, 1000이 출력됨을 확인할 수 있다. 비활성화 시킨 경우에는 두 개의 스레드가 하나의 변수(cnt)에 접근하여 1씩 증가 시키는 동작을 수행하는 도중, 스레드1 에서 2로(또는 반대)의 context switching이 발생됨으로 인해 원래의 값을 잃어버리는(또는 변형되는) 상황이 발생하기 때문이다. 반대로 뮤텍스를 사용하는 경우에는 하나의 스레드가 작업을 수행하고 마칠 때까지 (wait(lock)에서 release(unlock)까지) 다른 스레드는 대기(wait(release))하기 때문에 원하는 대로 2000의 결과를 확인할 수 있다.

댓글 없음:

댓글 쓰기