10.11(목) 실습-1( 뮤텍스, 세마포어, 프로세스감시, 스레드풀 )

2007/10/14 16:47

1. 뮤텍스
[ more.. | less.. ]
// 뮤텍스 : 자원의 독점.. 다중 프로세스의 다중 스레드 동기화
int main()
{
    HANDLE h = CreateMutex(
        0,          // 보안속성
        FALSE,      // 소유권 여부
        "m"         // 이름 - 생략가능
        );

    if( h == 0 )
        printf( "뮤텍스를 만들수 없다.\n");
    else
    {
        // Createxxx() 함수로 KO를 생성했을 떄 Create or Open 인지 알고 싶다면.
        // LastErrorCode를 확인한다.
        if( GetLastError() == ERROR_ALREADY_EXISTS )
            printf( " 뮤텍스를 open 했습니다.\n" );
        else
            printf( "뮤텍스를 create 했습니다.\n" );
    }
    printf("뮤텍스 대기\n");

    // 재귀 횟수를++; 소유권을 획득 한다. --Mutex.signalState
    DWORD ret = WaitForSingleObject( h, INFINITE );
    if( ret == WAIT_OBJECT_0 )  // 정상적으로 signal이 된 경우
        printf("뮤텍스 획득\n");
    else if( ret == WAIT_ABANDONED )
        printf("포기된 뮤텍스\n");
    else if( ret == WAIT_TIMEOUT )
        printf("대기 시간 종료\n");
    else if( ret == WAIT_FAILED )
        printf("Wait에 전달된 핸들이 잘못됨\n");

    getch();

    // 뮤텍스의 소유자는 뮤텍스의 signal 상태와 상관없이 wait를 통과할 수 있다.
    // 자신의 주머니에 열쇠가 있는데 다시 열쇠를 찾는 상황( DeadLock의 위험.. )
    //WaitForSingleObject( h, INFINITE );
    //printf("뮤텍스 획득\n");
    //getch();

  // ReleaseMutex를 하지 않는다면 포기된 뮤텍스가 발생한다.
   // ReleaseMutex( h );      // 재귀횟수--; 소유권을 반납.. ++Mutex.signalState
    CloseHandle( h );
}

2. 세마포어
[ more.. | less.. ]
  // Limit가 1인 세마포어는 뮤텍스와 비슷하다. 하지만 차이가 있다.
    // 뮤텍스 : 소유권 개념을 가진다. 자원을 획득한 스레드만 자원 반납이 가능하다.
    // 세마포어 : 소유의 개념이 없다. - count의 감소와 증가를 서로 다른 스레드가 해도 된다
int
main()
{
    HANDLE h = CreateSemaphore(
        0,      // 보안
        3,      // 카운트의 현재값 ( signalstate = 3 )
        3,      // 카운트 최대값   ( limit = 3 )
        "s" );  // 이름

    printf( "세마포어를 대기합니다.\n" );

    WaitForSingleObject( h, INFINITE ); // --signalstate

    printf( "세마포어 획득\n" );

    getch();

    LONG old;
    // 대부분 1을 증가한다. limit이상의 값은 의미가 없다.
    ReleaseSemaphore( h, 1, &old );     // signalstate += 2번째 인자만큼..

    CloseHandle( h );.
}

3. Event
[ more.. | less.. ]
// SetEvent.cpp
int main()
{
    HANDLE h = CreateEvent(
        0,      //보안
        FALSE// Reset의 종류( T:manual, F:auto )
        FALSE// 초기 signal 상태
        "e" );  // 이름

    while( 1 )
    {
        getch();

        SetEvent( h );  // Event를 Signal 상태로 한다.
                        // 결국 커널 함수인 KeSetEvent()를 호출하게 된다.
    }
    CloseHandle( h );
}

////////////////////////////////////////////////////////////////////////////////
// Event.cpp  이벤트 signal이 오기를 기달린다.

int main()
{
    HANDLE h = CreateEvent(
        0,      //보안
        FALSE// Reset의 종류( T:manual, F:auto )
        FALSE// 초기 signal 상태
        "e" );  // 이름

    printf("Event를 대기 합니다.\n");

    WaitForSingleObject( h, INFINITE );
    printf("Event 획득\n");

    CloseHandle( h );
}

4. 생산자와 소비자
[ more.. | less.. ]
queue<int> Q;   // 생산자와 소비자가 공유할 Q( 전역변수, 모든 data멤버를 스레드가 공유)
HANDLE hMutex;  // 접근을 동기화 하기 위해 사용한다.
HANDLE hSemaphore;  // Q에 이쓴ㄴ 자원의 갯수를 파악한다.(Empty일 경우를 대비)

// 생산자
DWORD WINAPI Produce(void* p)
{
    static int n = 0;

    while( 1 )
    {
        ++n;

        // Q에 대한 독점권을 획득한다.
        WaitForSingleObject( hMutex, INFINITE );
        ///////////////////////////////////////////////
        Q.push( n );  //생산
        printf("생산자 : %d\n", n );

        // 세마포어의 카운트를 올려준다.
        LONG old;
        ReleaseSemaphore( hSemaphore, 1, &old );
        //////////////////////////////////////////////
        // Q에 대한 독점권을 반납한다.
        ReleaseMutex( hMutex );

        Sleep( (rand() % 100) * 30 );    // 0~3초간 대기
    }
    return 0;
}

// 소비자
DWORD WINAPI Consume( void* p )
{
    while( 1 )
    {       
        // Q에 자원이 있는지를 파악한다. - 세마포어
        WaitForSingleObject( hSemaphore, INFINITE );
        // Q에 대한 독점권을 획득한다.
        WaitForSingleObject( hMutex, INFINITE );

        ///////////////////////////////////////////////
        int n = Q.front();
        Q.pop();
        printf("               소비자 : %d\n", n );
        //////////////////////////////////////////////
        // Q에 대한 독점권을 반납한다.
        ReleaseMutex( hMutex );

        Sleep( ( rand() % 100 ) * 30 );
    }
    return 0;
}

int main()
{
    // 생산자 소비자가의 Q의 동기화를 위해서 뮤텍스(크리티컬섹션)사용
    hMutex = CreateMutex( 0, 0, 0 );    // 이름이 필요없으면 0.

    // 초기 카운트 0, 최대 카운트 1000(Q의 크기)의 세마포어..
    hSemaphore = CreateSemaphore( 0, 0, 1000, 0 );

    srand( time(0) );
    HANDLE h[2];

    h[0] = CreateThread( 0, 0, Produce, 0, 0, 0 );
    h[1] = CreateThread( 0, 0, Consume, 0, 0, 0 );

    WaitForMultipleObjects( 2, h, TRUE, INFINITE );
}

5. STL list의 Thread Safe
[ more.. | less.. ]
// thread-safe class 만들기
// slist는 내부적으로 동기화를 해주게 된다.
// 이는 VC2005 내부적으로 구현이 되어 있다.

template<typename T> class slist
{
    CRITICAL_SECTION cs;

public:
    virtual void Lock() { EnterCriticalSection( &cs ); }
    virtual void Unlock() {  LeaveCriticalSection( &cs ); }
    void push_front( const T& n )
    {
        Lock();
        //....
        Unlock()
    }

    void pop_front()
    {
        Lock();
        //...
        Unlock();
    }
};

// 동기화가 싫다면 상속을 받아서 virtual 의 작업을 아무것도 하지 못하게 한다.
template<typename T> class slist_nosync : public slist<T>
{
    virtual void Lock() {}
    virtual void Unlock() {}
};


6. CriticalSection 단위전략 으로 사용해보기.
[ more.. | less.. ]
class UseCriticalSection
{
public:
    UseCriticalSection() { InitializeCriticalSection( &cs ); }
    ~UseCriticalSection() { DeleteCriticalSection( &cs ); }

    virtual void Lock() {  EnterCriticalSection( &cs ); }
    virtual void Unlock() { LeaveCriticalSection( &cs );  }
};
class NoThread
{
public:
    virtual void Lock() {}
    virtual void Unlock() {}
};
// 단위 전략 기반의 설계 기술.
template<typename T, typename ThreadModel = NoThread> class slist
{
    ThreadModel Sync;

public:
    virtual void Lock() { Sync.Lock(); }
    virtual void Unlock() {  Sync.Unlock(); }
    void push_front( const T& n )
    {
        Lock()
        //....
        Unlock()
    }

    void pop_front()
    {
        Lock();
        //...
        Unlock();
    }
};

slist< int, UseCriticalSection > s;

7. 스레드 풀 ( 세마포어를 이용 )
[ more.. | less.. ]
typedef void(*WORK)();  // 작업의 모양 - 결국 함수 포인터

queue<WORK> Q;  // 작업 Q ( VC2005의 STL은 스레드 안전하므로 동시접근해도 된다. )

BOOL bContinue = TRUE;

HANDLE hSemaphore;

// Thread Pool 에 놓인 모든 스레드가 실행하는 함수
DWORD WINAPI ThreadFunc( void* p )
{
    while( bContinue )
    {
        // 작업 Q에 작업이 들어 올때 까지 대기한다.
        WaitForSingleObject( hSemaphore, INFINITE );

        // Q에서 작업을 꺼낸다.
        //-------------------------------
        // 동기화가 필요하다!!!!!!!!!!
        WORK w = Q.front();
        Q.pop();
        //-------------------------------

        w(); // 작업을 수행한다.
    }
    return 0;
}
// 작업 Q에 작업을 넣는 함수.
void AddWork( WORK f )
{
    Q.push( f );

    LONG old;
    ReleaseSemaphore( hSemaphore, 1, &old );
}
// Pool을 초기화 한다.
void InitPool( int n )
{
    hSemaphore = CreateSemaphore( 0, 0, 1000, 0 );

    for( int i = 0; i < n; ++i )
        CloseHandle( CreateThread( 0, 0, ThreadFunc, 0, 0, 0 ) );
}
//--------------------------------------------------
// Pool에서 작업할 함수
void foo()
{
    for( int i = 0; i < 20; ++i )
    {
        printf( "%d : %d\n", GetCurrentThreadId(), i );
        Sleep(1000);
    }
}

int main()
{
    InitPool( 3 );

    // Pool에 작업을 넣는다.
    AddWork( foo ); AddWork( foo ); AddWork( foo );
    AddWork( foo ); AddWork( foo ); AddWork( foo );

    printf( "main...\n" );

    getch();
}

8. 프로세스 감시( 종료를 기달린다. )
[ more.. | less.. ]
DWORD WINAPI Monitor( void* p )
{
    DWORD pid = (DWORD)p;

    HANDLE hProcess = OpenProcess( SYNCHRONIZE, 0, pid );
    HANDLE hEvent = OpenEvent( EVENT_ALL_ACCESS, 0, // 상속가능여부
                                   "EXIT_EVENT" );  // 이름

    HANDLE h[2] = { hEvent, hProcess };
    DWORD ret = WaitForMultipleObjects( 2, h, FALSE, INFINITE );

    if( ret == WAIT_OBJECT_0 )  // h[0] 즉 event signal
        printf("Event가 signal이 되어서 종료합니다.");
    else if( ret == WAIT_OBJECT_0+1 ) // h[1] .. +63까지 가능..
        printf("\n %d 프로세스가 종료되었습니다. \n", pid );

    CloseHandle( hEvent );
    CloseHandle( hProcess );

    return 0;
}

int main()
{
    // 새롭게 생성되는 스레드와 통신하기 위한 manual Event
    HANDLE hEvent = CreateEvent( 0, TRUE, 0, "EXIT_EVENT" );
    int count = 0;
    HANDLE hThread[100];

    while( 1 )
    {
        int n;
        printf("대기할 프로세스 ID >> " );
        scanf("%d", &n);

        if ( n == -1 ) break;   // 루프 탈출

        // 새로운 스레드를 생성해서 감시를 하게 한다.
        hThread[count++] = CreateThread( 0, 0, Monitor, (void*)n, 0, 0 );
    }

    // 모든 작업중인 스레드를 깨워서 종료되게 한다.
    SetEvent( hEvent );

    // 모든 스레드가 죽을때까지 대기!!
    WaitForMultipleObjects( count, hThread, TRUE, INFINITE );
    for( int i = 0; i < count; ++i )    CloseHandle( hThread[i] );

    return 0;
}

Tags

System, 실습