1. 뮤텍스
2. 세마포어
3. Event
4. 생산자와 소비자
5. STL list의 Thread Safe
6. CriticalSection 단위전략 으로 사용해보기.
7. 스레드 풀 ( 세마포어를 이용 )
8. 프로세스 감시( 종료를 기달린다. )
more..
// 뮤텍스 : 자원의 독점.. 다중 프로세스의 다중 스레드 동기화
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 );
}
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..
// 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 );.
}
// 뮤텍스 : 소유권 개념을 가진다. 자원을 획득한 스레드만 자원 반납이 가능하다.
// 세마포어 : 소유의 개념이 없다. - 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..
// 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 );
}
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..
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 );
}
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..
// 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() {}
};
// 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..
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;
{
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..
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();
}
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..
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;
}
{
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;
}