티스토리 뷰

다중 쓰레드 환경에서 작업하다보면 쓰레드동기화 때문에 골치아픈경우가 매우 많다.

예를 들면 두 쓰레드가 공통객체를


같이 쓰게되는 경우이다. 

 



테스트했었던 동기화처리가 되지않은 코드이다.

 

#include <iostream>

#include "CThread.h" 

#include <stdio.h>

 

int money;

 

class ThreadTest : public CThread 

{

private:

    int num;

public:

    ThreadTest () { num = 0; }

    ThreadTest (int num){ this->num = num;}

    virtual int run ()

    {

//CThreadSync sync;

if(money >= 1000)

{

printf("[%d] 돈이 1000원 이상 있음 \n",num);

for(int idx=0; idx<10000; idx++)

{

//시간이조금 걸리는 어떤작업

}

money -= 1000;

printf("[%d] 인출 잔돈 : %d \n",num,money);

}

else

{

money += 1000;

printf("[%d] 입금 잔돈 : %d \n",num,money);

}

 

if(money != 1000 && money != 0)

{

printf("[%d] 에러 잔돈 : %d \n",num,money);

exit(1);

}

 

return 0;

    }

 

};

 

int main () 

{

    ThreadTest test1(3);

    ThreadTest test2(4);

 

money = 1000;

 

    test1.Start ();

    test2.Start ();

    while(1);


이런 경우는 다음과 같은 문제가 생길수 있다. 

 

두쓰레드가 동시에 1000원을 인출해서 남은돈이 -가 되는경우이다.

if문으로 처리 해놨지만 쓰레드동기화 관점에서 보면 의미가 없다. 

 


이쓰레드는 만약 다른가 쓰레드 공통객체를 사용중이라면 기다려준다는 보장이 없기때문에

에러를 낼 확률이 아주 많다. 물론 운이좋다면 에러가 안날때도 있다.

하지만 이건 눈을감고 지뢰밭을 걸어가는 거랑 똑같다.

정말 운이 좋으면 아무문제 없이 살지만 운이 없으면 발목이 날아간다!!

이러한 문제를 해결하려면 동기화객체를 만들어서 임계영역을 지정해줘야한다.

나는 동기화객체로 pthread_mutex_t 를 사용했다.

임계영역을 지정하기위한 클래스이다.

CCriticalSection.h 

 

#include <windows.h>

#include <pthread.h>

class CCriticalSection

{

public:

CCriticalSection(VOID)

{

pthread_mutexattr_t attr;

pthread_mutexattr_init ( &attr );

pthread_mutexattr_settype ( &attr, PTHREAD_MUTEX_RECURSIVE );

pthread_mutex_init(&mSync,&attr);

}

~CCriticalSection(VOID)

{

pthread_mutex_destroy(&mSync);

}

 

inline VOID Enter(VOID)

{

pthread_mutex_lock(&mSync);

}

 

inline VOID Leave(VOID)

{

pthread_mutex_unlock(&mSync);

}

 

private:

pthread_mutex_t mSync;

};

임계영역을 이용해서 멀티쓰레드 환경에서 동기화를 보장해주는 코드이다.

CMultiThreadSync.h
 

 

#include "CCriticalSection.h"

 

template <class T>

class CMultiThreadSync

{

friend class CThreadSync;

public:

class CThreadSync

{

public:

CThreadSync(VOID)

{

T::mSync.Enter();

}

 

~CThreadSync(VOID)

{

T::mSync.Leave();

}

};

private:

static CCriticalSection mSync;

};

 

template<class T>

CCriticalSection CMultiThreadSync<T>::mSync;

이 코드는 클래스단위로 동기화가 이루어진다. 즉 CMultiThreadSync를 상속받은

클래스의 객체들은 임계영역 지정시 해당 클래스 객체들간에 동기화를 보장한다는 것이다. ​

 

임계영역을 사용하고싶은 객체에 CMultiThreadSync를 상속받고

임계영역의 시작지점에 
CThreadSync 객체를 생성하면 된다.

그러면 객체의 생성시점에 lock을 호출하고 소멸시점에 unlock을 호출한다.


이제 동기화가 보장안되였던 쓰레드에 동기화를 보장시켜보자.

main.cpp 
 

#include <iostream>

#include "CThread.h" 

#include <stdio.h>

#include "CMultiThreadSync.h"

 

int money;

 

class ThreadTest : public CThread , public CMultiThreadSync<ThreadTest>

{

private:

    int num;

public:

    ThreadTest () { num = 0; }

    ThreadTest (int num){ this->num = num;}

    virtual int run ()

    {

CThreadSync sync; // 임계영역 시작지점

if(money >= 1000)

{

printf("[%d] 돈이 1000원 이상 있음 \n",num);

for(int idx=0; idx<10000; idx++)

{

//시간이조금 걸리는 어떤작업

}

money -= 1000;

printf("[%d] 인출 잔돈 : %d \n",num,money);

}

else

{

money += 1000;

printf("[%d] 입금 잔돈 : %d \n",num,money);

}

 

if(money != 1000 && money != 0)

{

printf("[%d] 에러 잔돈 : %d \n",num,money);

exit(1);

}

return 0;

    }

 

 

};

 

int main () 

{

    ThreadTest test1(3);

    ThreadTest test2(4);

 

money = 1000;

 

    test1.Start ();

    test2.Start ();

Sleep(1000);

    //while(1);

test2.Stop ();

    test1.Stop ();

}

다음은 결과화면이다.

 

프로그램이 끝날때까지 동기화가 잘 이루어진다. 

여기서는 전역변수 money에 임계영역 설정을 위해서 쓰레드에 동기화 처리를 했지만 쓰레드 자체에 동기화처리보단

실제로는 공통으로 사용하는 객체(예를 들어 큐,스택에 PUSH,POP등)에 동기화 처리를 하는게 더 효율적이다.

누군가에게는 작은도움이 되었기를 바라면서 오늘의 포스팅 끝~

댓글