윈도우 에서의 쓰레드의 동기화  시스템프로그래밍 

2013.04.04. 15:33  수정  삭제

복사http://blog.naver.com/pwk0810/40186296925

전용뷰어 보기

출처 : 열혈 강의 TCP/IP Programming

+ 유저 모드와 커널 모드

유저 모드

 

  • 응용 프로그램이 실행되는 기본 모드로, 물리적인 영역으로의 접근이 허용되지 않으며,  접근할 수있는 메모리의 영역에도 제한된다.
  • 응용 프로그램의 실행 모드
  • 유저모드로 실행되는 과정에서는 운영체제와 관련된 메모리 영역이 보호 받는다.
  • 쓰레드와 같이 커널 오브젝트의 생성을 동반하는 리소스의 생성을 위해서는 다음 모드 변환의 과정을 기본적으로 거친다.
    • 유저모드 -> 커널 모드 -> 유저모드 ( 확인할 수 있는 방법이?? )
    • 유저 모드에서 커널 모드는 리소스의 생성을 위한 것
    • 커널 모드에서 유저 모드의 전환 : 응용 프로그램의 나머지 부분을 이어서 실행하기 위한것.
    • 빈번한 모드의 변환은 성능에 영향을 줄 것.

커널 모드
  • 운영체제가 실행될 때의 모드로, 메모리 뿐만 아니라 , 하드웨어의 접근에도 제한이 따르지 않는다.
  • 운영체제의 실행 모드
+유저모드 동기화
  • 운영체제의 도움 없이 응용 프로그램상에서 진행되는 동기화가 유저 모드 동기화.
  • 속도가 빠르다.
  • 운영체제의 힘을 빌리지 않기 때문에 기능은 제한적
  • CRITICAL_SECTION 기반의 동기화가 그 예
+ 커널 모드 동기화
  • 유저모드 동기화에 비해 제공되는 기능이 더 많다.
  • DEAD_LOCK에 걸리지 않도록 타임아웃의 지정이 가능하다.
  • 서로 다른 프로세스에 포함되어 있는 두 쓰레드간의 동기화가 가능하다.
  • 유저모드에서는 커널 모드로 , 다시 유저모드로의 빈번한 변환이 불가피 하기 때문에 성능에 있어서의 제약이 따른다.
+DEADLOCK
  • 임계영역에 진입을 대기중인, 블록킹 상태에 놓여 있는 쓰레드가 이를 빠져 나오지 못하는 상황을 의미.
+CRITICAL_SECTION 기반의 동기화 ( 유저 모드 동기화 기법 )
  • CRITICAL_SECTION OBJECT 라는 것을 생성해서 이를 동기화에 활용
  • 커널 오브젝트가 아님
  • criticalSection 생성과 소멸 함수.
  • void InitializeCriticalSection(LPCRITICAL_SECTION lpCriticalSection);
  • void DeleteCriticalSection(LPCRITICAL-SECTION lpCriticalSection);

  • void EnterCriticalSection(LPCRITICAL_SECTION lpCriticalSection);
  • void LeaveCriticalSection(LPCRITICAL_SECTION lpCriticalSection);
          criical_section 획득 및 반납 함수.


void SyncCS_WinMain(int argc, TCHAR* argv[])
{
_tprintf(_T("SyncCS_WinMain\n"));
HANDLE tHanles[NUM_THREAD];
int i;

InitializeCriticalSection(&cs);
for(i = 0; i < NUM_THREAD; i++)
{
if(i%2)
tHanles[i] = (HANDLE)_beginthreadex(NULL, 0 , threadDec, NULL, 0 , NULL);
else
tHanles[i] = (HANDLE)_beginthreadex(NULL, 0 , threadInc, NULL, 0 , NULL);
}

WaitForMultipleObjects(NUM_THREAD, tHanles, TRUE, INFINITE);
DeleteCriticalSection(&cs);

_tprintf(_T("result : %lld\n"), num );
}

unsigned WINAPI threadInc(void *arg)
{
EnterCriticalSection(&cs);
int i =0;
_tprintf(_T("threadinc\n"));
for(i = 0; i < 5; i++)
{
num += 1;
_tprintf(_T("threadInc num  %d\n"), num);
}
LeaveCriticalSection(&cs);
return 0;
}

unsigned WINAPI threadDec(void *arg)
{
EnterCriticalSection(&cs);
int i =0;

_tprintf(_T("threaddec\n"));

for(i = 0; i < 5; i++) 
{ num -= 1; _tprintf(_T("thread dec num %d\n"), num); } 
LeaveCriticalSection(&cs); 
return 0; 
}

커널 모드 동기화

+ Mutex(Mutual Exclusion ) 오브젝트 동기화

#include HANDLE CreateMutex(LPSECURITY_ATTRIBUTES lpMutexAtrributes, BOOL InitialOwner, LPCTSTR lpName);
-> 성공시 생성된 Mutex 오브젝트 핸들, 실패시 NULL 반환

HANDLE hMutex = CreateMutex(NULL, FALSE, NULL);

BOOL CloseHandle(HANDLE hObject);

BOOL ReleaseMutex(HANDLE hMutex);
  • Mutex는 소유되었을 때 non-signaled 상태가 되고,  반납되었을때 signal 상태가 되는 커널 오브젝트이다.
  • WaitForSingleObject(hMutex, INFINITE)
  • // 임계 영역 시작.

  • RelaseMutex(hMutex); // 임계 영역 끝

+ 세마 포어 오브젝트 기반 동기화

  • 리눅스의 바이너리 세마포어와 비슷하며 세마포어 커널 오브젝트에 등록이 된다.
  • CreateSemaphore(LPSECRITY_ATTRIBUTES lpSemaphoreAttributes, LONG lIntialCount , LONG lMaxiumCount, LPCTSTR lpName);
    • 세마 포어 값이 0인 경우 non-signal 상태가 되고, 0 보다 큰 경우 signal 상태가 되는 특성을 이용해서 동기화가 진행
    • 매개변수 maximCount를 3으로 전달하면, 세 개의 쓰레드가 동시에 임계 영역에 진입하는 유형의 동기화도 가능하다.
  • BOOL RealseSemaphore(HANDLE hSemaphore, LONG lReleaseCount, LPLONG lpPreviosCount);
  • WaitForSingleObject(hSemaphore, INFINITE); // 임계 영역의 시작
  • RelaseSemaphore(hSemaphore, 1, NULL );   // 임계 영역의 끝


static int num;

static HANDLE semOne;
static HANDLE semTwo;


void SyncSema_WinMain(int argc, TCHAR* argv[])
{
_tprintf(_T("SyncSema_WinMain\n"));
HANDLE hThread1, hThread2; 

semOne = CreateSemaphore(NULL, 0, 1, NULL);
semTwo = CreateSemaphore(NULL, 1, 1, NULL);

hThread1 = (HANDLE)_beginthreadex(NULL, 0 , Read, NULL, 0 , NULL);
hThread2 = (HANDLE)_beginthreadex(NULL, 0 , Accu, NULL, 0 , NULL);

WaitForSingleObject(hThread1, INFINITE);
WaitForSingleObject(hThread2, INFINITE);


CloseHandle(semOne);
CloseHandle(semTwo);

}

unsigned WINAPI Read(void *arg)
{
int i =0;
//_tprintf(_T("threadRead\n"));

for(i = 0; i < 5; i++)
{
WaitForSingleObject(semTwo, INFINITE);
_fputts(_T("InputNum :"),stdout);
_tscanf(_T("%d"),&num);
ReleaseSemaphore(semOne, 1, NULL);
}
return 0;
}

unsigned WINAPI Accu(void *arg)
{
int i , sum = 0;

for(i = 0; i < 5; i++)
{
WaitForSingleObject(semOne, INFINITE);
sum+=num;
_tprintf(_T("threadAcc sum : %d i :%d\n"), sum , i);
ReleaseSemaphore(semTwo, 1, NULL);
}

_tprintf(_T("threadAcc sum : %d \n"), sum);
return 0;
}

+Event 오브젝트 기반 동기화

HANDLE CreateEvent(LPSECURITY_ATTIRBUTE lpEventAttribute, BOOL bManualRest, BOOL bInitialState, LPCTSTR lpName);
  •  bManualReset:  TRUE 가 전달되면, manual - reset모드의 Event  오브젝트가 생성되고, 이렇게 되면 WaitForSingleObject 함수가 반환된다고 해서 non-signal  상태가 되지 않는다.
  • 명시적으로 오브젝트 상태를 변경해야 한다
  • BOOL ResetEvent(HANDLE hEvent);
  • BOOL SetEvent(HANDLE hEvent);

 

Posted by 뉴암스테르담
l