티스토리 뷰
1.Critical Section
- 유저 레벨의 동기화 방법 중, 유일하게 커널 객체를 사용하지 않음. - 내부 구조가 단순하여 동기화 처리에 대한 속도가 빠르다. - 동일한 프로세스 내에서만 사용. - 커널 객체를 사용하지 않기 때문에 핸들을 사용하지 않고, CRITICAL_SECTION 이라는 타입을 정의하여 사용. |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | // 크리티컬 섹션을 초기화한다. // 파라메터는 여러 개의 쓰레드에 참조되어야 하므로 전역으로 선언하도록 한다. void InitializeCriticalSection(LPCRITICAL_SECTION lpCriticalSection); // 생성된 크리티컬 섹션을 삭제한다. void DeleteCriticalSection(LPCRITICAL_SECTION lpCriticalSection); // 동기화 방법 void workFunc() { EnterCriticalSection(LPCRITICAL_SECTION lpCriticalSection); // 호출. lock. // 여기서 공유 자원을 안전하게 액세스한다. LeavCriticalSection(LPCRITICAL_SECTION lpCriticalSection); // 호출. unlock. } |
2. 동기화 대기 함수
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | DWORD WaitForSingleObject( HANDLE hHandle, DWORD dwMiliseconds); // hHandle : 동기화 객체. dwMiliseconds : 대기 시간 // 대기 시간을 INFINITE로 지정하면 무한대로 대기한다. // 반환 값의 종류는 세가지다. // WAIT_OBJECT_0 : 성공 // WAIT_TIMER : 동기화 객체가 시그널 상태 // WAIT_ABANDONED : 대기시간 초과 DWORD WaitForMulipleObject( DWORD nCound, CONST HANDLE *lpHandles, BOOL fWaitAll, DWORD dwMiliseconds); // WaitforSingleObject 함수는 하나의 객체에 대한 동기화를 기다리는데 비해 이 함수는 // 복수 개의 동기화 객체를 대기할 수 있다. 동기화 객체의 핸들 배열을 만든 후 lpHandles 인수로 // 배열의 포인터를 전달해 주고 nCount로 배열의 개수를 넘겨준다. // fWaitAll이 TRUE이면 모든 동기화 객체가 시그널 상태가 될 때까지 대기하며, // FALSE이면 그 중 하나라도 시그널 상태가 되면 대기 상태를 종료한다. // 리턴 값의 의미는 조금 다르다. // WAIT_TIMEOUT : 대기시간 초과 // WAIT_OBJECT_0 : bWaitAll이 TRUE일 때 - 모든 동기화 객체가 시그널 상태. // FALSE일 때 - lpHandles 배열에서 시그널 상태가 된 동기화 객체의 인덱스를 반환. // 이 경우 lpHandles[리턴값 - WAIT_OBJECT_0]의 방법으로 시그널 상태가 된 // 동기화 객체의 핸들을 얻을 수 있다. |
3. Mutex (MUTual EXclusion)
- 최초 Signaled 상태로 생성되며, WaitforSingleObject()와 같은 대기 함수를 호출함으로써 NonSignaled 상태가 된다. - 크리티컬 섹션에 비해서 느리다. 크리티컬 섹션의 경우 구조체의 값을 통해 잠그기를 허용하는데 비해 뮤텍스는 객체를 생성하기 때문이다. - 만약 A라는 쓰레드가 뮤텍스를 소유하고 있고, B라는 쓰레드가 뮤텍스를 사용하기 위하여 대기하고 있을 때, A 쓰레드가 잘못된 연산을 수행하거나 강제 종료되어서 소유하고 있던 뮤텍스를 반환하지 않았을 때에 B라는 쓰레드는 뮤텍스를 얻기 위해 무한정 기다려야 할까? => 만약 크리티컬 섹션을 사용하였다면 B 쓰레드는 무한정 대기하기 된다.(데드락) 하지만, 뮤텍스의 경우는 자신이 소유한 쓰레드가 누군지 기억하고 있다. 그리고 Windows 운영체제에서 뮤텍스를 반환하지 않는 상태에서 쓰레드가 종료될 경우 그 뮤텍스는 강제적으로 Signaled 상태로 만든다. - 만약 같은 쓰레드가 중복으로 뮤텍스를 호출할 경우 데드락이 발생할까? => 발생하지 않는다. 왜냐면 같은 쓰레드가 중복으로 뮤텍스를 호출할 경우는 내부 Count만 증가시키고, 진입은 허용한다. 그리고 나중에 내부 Count가 '0'으로 될 때 Singaled 상태로 해준다. (이 부분은 크리티컬 섹션도 동일한 개념이다.) |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | HANDLE CreateMutex(LPSECURITY_ATTRIBUTES lpMutexAttributes, BOOL bInitialOwner, LPCTSTR lpName); // lpMutexAttributes는 보안 속성으로 보통 NULL로 지정한다. // bInitialOwner는 뮤텍스 생성과 동시에 소유할 것인지 지정하는데 TRUE이면 이 쓰레드가 바로 뮤텍스를 // 소유하면서 다른 쓰레드는 소유할 수 없게 된다. // lpName은 뮤텍스의 이름이다. NULL 설정 가능. // 리턴 값은 뮤텍스의 핸들이다. HANDLE OpenMutex( DWORD dwDesiredAccess, BOOL bInheritHandle, LPCTSTR lpName); // 뮤텍스를 연다. 프로세스 ID와 마찬가지로 뮤텍스의 이름은 전역적으로 유일하다. BOOL ReleaseMutex( HANDLE hMutex); // 해당 쓰레드의 뮤텍스 소유를 해제하여 다른 쓰레드가 가질 수 있도록 한다. HRESULT CloseHandle( HANDLE hHandle); // 모든 커널 객체와 마찬가지로 생성된 뮤텍스를 파괴할 때 사용한다. // 리턴 값 S_OK : 성공. // 그외엔 에러. |
* 포기된 뮤텍스
만약 뮤텍스를 소유하고 있는 쓰레드가 ExitThread나 TerminateThread로 비정상적으로 종료시켰을 경우 강제로 뮤텍스를 시그널 상태로 만들어준다. 그러므로 대기 중인 다른 쓰레드에서 뮤텍스를 가지게 되는데, WaitForSingleobject() 의 리턴 값으로 WAIT_ABANDONED 값을 전달 받음으로써 이 뮤텍스가 정상적인 방법으로 신호상태가 된 것이 아니라 포기된 상태임을 알 수 있다. 중복 소유 뮤텍스를 여러 번 겹쳐서 사용했을 경우 데드락과 같은 상태에 빠질 수도 있을 것이다.
4. Semaphore
- 뮤텍스와 유사한 동기화 객체이다. 뮤텍스는 하나의 공유 자원을 보호하는데 비해, 세마포어는 일정 개수를 가지는 자원을 보호할 수 있다. 여기서 자원이라는 것은 윈도우, 프로세서, 쓰레드와 같은 SW적이거나 어떤 권한과 같은 무형적인 것도 포함된다. |
1 2 3 4 5 6 7 8 9 10 11 12 13 | HANDLE CreateSemaphore(LPSECURITY_ATTRIBUTES lpSemaphoreAttributes, LONG iInitialCount, LONG iMaximumCount, LPCTSTR lpName); // iMaximumCount는 최대 사용 개수. // iInitialCount 에 초기값을 지정. // 아주 특별한 경우외엔 이 두가지 값은 같다. 세마포어는 뮤텍스와 같이 이름을 가질 수 있고 이름을 알고 있는 // 프로세스는 언제든지 OpenSemaphore로 핸들을 구할 수 있다. 역시 파괴할 때는 CloseHandle 함수를 이용한다. HANDLE OpenSemaphore( DWORD dwDesiredAccess, BOOL bInheritHandle, LPCTSRT lpName); // 뮤텍스와 같다. BOOL ReleaseSemaphore( HANDLE hSemaphore, LONG iReleaseCount, LPLONG lpPreviousCount); // iReleaseCount로 사용한 자원의 개수를 알려준다. lpPreviousCount는 세마포어 이전 카운트를 리턴받기 위한 // 참조 인수이다. NULL 가능. |
5. Event
- 위의 동기화 객체들이 공유자원을 보호하기 위해 사용되는데 비해 이벤트는 쓰레드의 작업 순서나 시기를 조정하기 위해 사용된다. |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | HANDLE CreateEvent(LPSECURITY_ATTRIBUTES lpEventAttributes, BOOL bManualReset, BOOL bInitialState, LPCTSTR lpName); // bManualReset은 이벤트가 수동 리셋(쓰레드가 Non-Signaled로 만들어 줄 때 까지 신호 상태를 유지)인지 // 자동리셋(대기 상태가 종료되면 자동으로 Non-Signaled가 된다.) 인지를 결정한다. TRUE이면 수동이다. // bInitialState가 TRUE이면 자동으로 Signaled 상태로 들어가 이벤트를 기다리는 쓰레드가 곧바로 실행할 수 있게 된다. HANDLE OpenEvent( DWORD dwDesiredAccess, BOOL bInheritHandle, LPCTSRT lpName); // 다른 부분과 같다. BOOL SetEvent( HANDLE hEvent); // 다른 동기화 객체와는 다르게 사용자 임의로 Signaled 상태와 Non-Signaled 상태를 설정할 수 있다. // 위의 함수는 Signaled 상태로 만들어 준다. BOOL ResetEvent( HANDLE hEvent); // Non-Signaled 상태로 만든다. 일반적으로 자동 리셋을 사용하는데, 이벤트를 발생시켜 대기 상태를 풀 때 // 자동으로 Non-Signaled 상태로 만드는 것이다. 하지만 여러 개의 쓰레드를 위해서 이벤트를 사용한다면 // 문제가 될 수도 있다. 그러므로 수동 리셋으로 이벤트를 생성 후 ResetEvent 함수로 수동리셋을 한다.
|
'General > Parerell' 카테고리의 다른 글
Thread design - #01 우리는 이미 알고 있었다 (0) | 2014.12.09 |
---|---|
non-blocking multithread programming (0) | 2014.12.09 |
C++ 기반 멀티스레드 프로그래밍을 몇년간 해오고 나서 든 생각 (0) | 2013.04.09 |
거짓 공유 (false sharing) (0) | 2010.08.19 |
멀티 스레드 정책 (0) | 2010.03.16 |
- Total
- Today
- Yesterday
- 엘키
- TraceRoute
- 조엘 온 소프트웨어
- ruby
- c언어
- SQLite Spy
- EasyExec
- 루비 온 레일즈
- 임백준
- CppSQLite
- NDC2013
- ftp
- 디버깅
- perfmon
- 게임개발포에버
- EzShortcut
- SDL
- svn
- MS-SQL
- 멀티스레드
- 게임데브포에버
- 리버스 엔지니어링
- RoR
- 바로가기 프로그램
- 디자인 패턴
- 루비
- 좋은 프로그래머
- Rails
- TDD
- Ruby on Rails
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | |||||
3 | 4 | 5 | 6 | 7 | 8 | 9 |
10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 | 18 | 19 | 20 | 21 | 22 | 23 |
24 | 25 | 26 | 27 | 28 | 29 | 30 |