2010년 7월 8일 목요일

동기화 기법

쓰레드 동기화 기법

 

 같은시간, 오직 하나의 쓰레드 또는 제한된 쓰레드만이 공유영역(Data 영역, 힙 영역) 에 접근하기 위한 방법

 

Critical Section 는 단일 프로세스의 스레드에 대해서만 동작합니다.반면에, 뮤텍스는 여러 프로세스의 스레드에 대해서도 동작합니다.세마포어는 CS, 뮤텍스가 가지는 특징에 하나를 더 가집니다. 세마포어는 특정 영역의 코드를 실행하는 스레드의 최대 개수를 설정할 수 있습니다.

 

  • 커널 오브젝트의 두가지 상태

프로세스 생성시 : Non-Signaled 상태로 생성

프로세스 종료시 : Signaled 상태로 변경됨

 

  •  커널 오브젝트의 상태를 확인하는 용도의 함수

DWORD  WaitForSingleObject( HANDLE hHandle, DWORD dwMilliseconds)

hHandle : 상태 확인을 원하는 커널 오브젝트의 핸들

dwMilliseconds: 커널 오브젝트가 Signaled 상태가 될때까지 기다릴 수 있는 최대 시간, INFINITE 로 설정시 프로세스 종료시까지 무한정 기다림

 

 

메모리 접근 동기화#

 

유저모드 동기화#

 

크리티컬 섹션(Critical Section) 기반의 동기화#

 

  1. // 크리티컬 섹션 오브젝트 선언
  2. CRITICAL_SECTION   hCriticalSection;
  3. // 초기화
  4. InitializeCriticalSection(&hCriticalSection);
  5. EnterCriticalSection (&hCriticalSection); // 임계영역 진입을 위해 크리티컬 섹션 오브젝트 획득
  6. // Code..
    LeaveCriticalSection (&hCriticalSection); // 크리티컬 섹션 오브젝트 반환
  7. // 리소스 반환
  8. DeleteCriticalSection(&hCriticalSection);

 

예제)

  1. #include <stdio.h>
    #include <windows.h>
    #include <process.h>
    #include <tchar.h>
  2. #define NUM_OF_GATE  6
  3. LONG gTotalCount = 0;
  4. CRITICAL_SECTION   hCriticalSection;

  5. void IncreaseCount()
    {
     EnterCriticalSection (&hCriticalSection);
     gTotalCount++;
     LeaveCriticalSection (&hCriticalSection);
    }

  6. unsigned int WINAPI ThreadProc( LPVOID lpParam )
    {
     for(DWORD i=0; i<1000; i++)
     {
      IncreaseCount();
     }
  7.  return 0;
    }

  8. int _tmain(int argc, TCHAR* argv[])
    {
     DWORD dwThreadId[NUM_OF_GATE];
     HANDLE hThread[NUM_OF_GATE];
  9.  InitializeCriticalSection(&hCriticalSection);
  10.  for(DWORD i=0; i<NUM_OF_GATE; i++)
     {
      hThread[i] = (HANDLE)
       _beginthreadex (
       NULL,
       0,          
       ThreadProc,    
       NULL,                  
       CREATE_SUSPENDED,    
       (unsigned *)&dwThreadId[i]  
       );
  11.    if(hThread[i] == NULL)
       {
        _tprintf(_T("Thread creation fault! \n"));
        return -1;
       }
     }
  12.  for(DWORD i=0; i<NUM_OF_GATE; i++)
     {
      ResumeThread(hThread[i]);
     }

  13.  WaitForMultipleObjects(NUM_OF_GATE, hThread, TRUE, INFINITE);
  14.  _tprintf(_T("total count: %d \n"), gTotalCount);
  15.  for(DWORD i=0; i<NUM_OF_GATE; i++)
     {
      CloseHandle(hThread[i]);
     }
  16.  DeleteCriticalSection(&hCriticalSection);
  17.  return 0;
    }

 

인터락 함수(Interlocked Family Of Function) 기반의 동기화#
  1. // 전역으로 선언된 하나의 변수를 증가 또는 감소 시키기위해 사용되는 특화된 함수
  2. LONG  InterlockedIncrement(LONG volatile* Addend);
  3. LONG  InterlockedDecrement(LONG volatile* Addend);

 

 

 

커널모드 동기화#

 

뮤텍스(Mutex) 기반의 동기화#

 

  1. HANDLE hMutex;
  2. // Mutex 생성
  3.  hMutex = CreateMutex( NULL, FALSE, _T("NamedMutex"));
  4. // Mutex 획득
  5. WaitForSingleObject(hMutex, INFINITE);
  6. //  Mutex 반환
  7. ReleaseMutex(hMutex);

 

 

  1. #include <stdio.h>
    #include <windows.h>
    #include <process.h>
    #include <tchar.h>
  2. #define NUM_OF_GATE  6
  3. LONG gTotalCount = 0;
    HANDLE hMutex;
  4. void IncreaseCount()
    {
     AcquireMutex(hMutex);
  5.  gTotalCount++;
  6.  ReleaseMutex(hMutex);
    }

  7. unsigned int WINAPI ThreadProc( LPVOID lpParam )
    {
     for(DWORD i=0; i<1000; i++)
     {
      IncreaseCount();
     }
  8.  return 0;
    }
  9. DWORD AcquireMutex(HANDLE mutex)
  10. {
  11. return WaitForSingleObject(mutex, INFINITE);
  12. }
    int _tmain(int argc, TCHAR* argv[])
    {
        DWORD dwThreadIDs[NUM_OF_GATE];
        HANDLE hThreads[NUM_OF_GATE];

  13.     hMutex = CreateMutex (
                 NULL,     // 디폴트 보안관리자.
                       FALSE,    // 누구나 소유 할 수 있는 상태로 생성.
                       NULL      // numaned mutex
           );
  14.     if (hMutex == NULL)
        {
            _tprintf(_T("CreateMutex error: %d\n"), GetLastError());
        }
  15.  for(DWORD i=0; i<NUM_OF_GATE; i++)
     {
           hThreads[i] = (HANDLE)
          _beginthreadex (
           NULL,
           0,          
           ThreadProc,    
           NULL,                  
           CREATE_SUSPENDED,    
           (unsigned *)&dwThreadIDs[i]  
             );
  16.      if(hThreads[i] == NULL)
         {
             _tprintf(_T("Thread creation fault! \n"));
             return -1;
         }
     }
  17.  for(DWORD i=0; i<NUM_OF_GATE; i++)
     {
      ResumeThread(hThreads[i]);
     }
     WaitForMultipleObjects(NUM_OF_GATE, hThreads, TRUE, INFINITE);
  18.  _tprintf(_T("total count: %d \n"), gTotalCount);
  19.  for(DWORD i=0; i<NUM_OF_GATE; i++)
     {
      CloseHandle(hThreads[i]);
     }

     CloseHandle(hMutex);
  20.  return 0;
    }

 

세마포어(Semaphore) 기반의 동기화#

뮤텍스와 비슷하나 세미포어는 카운트 기능을 가지고 있음

임계영역에 접근 가능한 쓰레드의 개수를 조절하는 기능 내포

- 임계 영역의 접근 허용 쓰레드 개수를 하나로 제한하기 위해 사용되는 세마포어를 바이너리 세마포어라 하고, 이는 즉 뮤텍스와 동일한 기능

 

  1. // 세마포어 변수 설정
  2. HANDLE hSemaphore;
  3. // 세마포어 생성
  4.     hSemaphore = CreateSemaphore (
                       NULL,    // 디폴트 보안관리자.
                       TABLE_CNT,      // 세마포어 초기 값.
                       TABLE_CNT,      // 세마포어 최대 값.
                       NULL     // unnamed 세마포어 구성.
                        );
  5. // WaitForSingleObject(hSemaphore, INFINITE); // 세마포어 획득
  6. // code...
  7. // ReleaseSemaphore(hSemaphore,1,NULL); // 세마포어 반환

 

  1.  /*
     MyongDongKyoJa.cpp
     프로그램 설명: 카운트 세마포어에 대한 이해
     시뮬레이션 제한 요소:
      1. 테이블이 총 10개이고, 동시에 총 10분의 손님만 받을 수 있다고 가정한다.
      2. 오늘 점심시간에 식사하러 오실 예상되는 손님의 수는 총 50분이다.
      3. 각 손님들께서 식사 하시는 시간은 대략 10분에서 30분 사이이다.
    */

  2. #include <stdio.h>
    #include <stdlib.h>
    #include <time.h>
    #include <windows.h>
    #include <process.h>
    #include <tchar.h>
  3. #define NUM_OF_CUSTOMER 50
    #define RANGE_MIN 10
    #define RANGE_MAX (30 - RANGE_MIN)
    #define TABLE_CNT 10

  4. HANDLE hSemaphore;
    DWORD randTimeArr[50];
  5. void TakeMeal(DWORD time)
    {
     WaitForSingleObject(hSemaphore, INFINITE);
     _tprintf( _T("Enter Customer %d~ \n"), GetCurrentThreadId());
  6.  _tprintf(_T("Customer %d having launch~ \n"), GetCurrentThreadId());
     Sleep(1000 * time); // 식사중인 상태를 시뮬레이션 하는 함수.
  7.  ReleaseSemaphore(hSemaphore, 1, NULL);
     _tprintf( _T("Out Customer %d~ \n\n"), GetCurrentThreadId());
    }

  8. unsigned int WINAPI ThreadProc( LPVOID lpParam )
    {
     TakeMeal((DWORD)lpParam);
     return 0;
    }

  9. int _tmain(int argc, TCHAR* argv[])
    {
        DWORD dwThreadIDs[NUM_OF_CUSTOMER];
        HANDLE hThreads[NUM_OF_CUSTOMER];
     
     srand( (unsigned)time( NULL ) );   // random function seed 설정

  10.  // 쓰레드에게 전달할 random 값 총 50개 생성.
     for(int i=0; i<NUM_OF_CUSTOMER ;i++)
     {
            randTimeArr[i] = (DWORD) (
        ((double)rand() / (double)RAND_MAX) * RANGE_MAX + RANGE_MIN
       );
       }
  11.  // 세마포어 생성.
        hSemaphore = CreateSemaphore (
                 NULL,    // 디폴트 보안관리자.
                       TABLE_CNT,      // 세마포어 초기 값.
                       TABLE_CNT,      // 세마포어 최대 값.
           NULL     // unnamed 세마포어 구성.
               );
        if (hSemaphore == NULL)
        {
            _tprintf(_T("CreateSemaphore error: %d\n"), GetLastError());
        }

  12.  // Customer를 의미하는 쓰레드 생성.
     for(int i=0; i<NUM_OF_CUSTOMER; i++)
     {
            hThreads[i] = (HANDLE)
          _beginthreadex (
           NULL,
           0,          
           ThreadProc,    
           (void*)randTimeArr[i],                  
           CREATE_SUSPENDED,    
           (unsigned *)&dwThreadIDs[i]  
             );
  13.      if(hThreads[i] == NULL)
         {
             _tprintf(_T("Thread creation fault! \n"));
             return -1;
         }
     }
  14.  for(int i=0; i<NUM_OF_CUSTOMER; i++)
     {
      ResumeThread(hThreads[i]);
     }
  15.  WaitForMultipleObjects(NUM_OF_CUSTOMER, hThreads, TRUE, INFINITE);
  16.     _tprintf(_T("----END-----------\n"));
  17.  for(int i=0; i<NUM_OF_CUSTOMER; i++)
     {
      CloseHandle(hThreads[i]);
     }
     
     CloseHandle(hSemaphore);
  18.  return 0;
    }

 

 

이름있는 뮤텍스(Named Mutex) 기반의 프로세스 동기화#

뮤텍스는 커널 오브젝트이므로 프로세스에 소속되는것이 아니라 운영체제가 관리한다. 따라서 뮤텍스에 유일한 이름을 지어주고 이것을 다른 프로세스에서 획득하여 사용하는것이 가능하다. 아래는 서로다른 프로세스에 속해있는 두개의 쓰레드를 동기화 하는 예제 코드이다.

 

  1. HANDLE OopenMutex(
  2. DWORD dwDesiredAccess, // 접근권한
  3. BOOL bInheritHandle, // 핸들 상속여부
  4. LPCSTR lpName // 뮤텍스 오브젝트 이름
  5. );

 

 

  1. #include <stdio.h>
    #include <windows.h>
    #include <process.h>
    #include <tchar.h>

  2. HANDLE hMutex;
    DWORD dwWaitResult;
  3. void ProcessBaseCriticalSection()
    {
     dwWaitResult = WaitForSingleObject(hMutex, INFINITE);
  4.  switch (dwWaitResult)
     {
      // 쓰레드가 뮤텍스를 소유하였다.
     case WAIT_OBJECT_0:
      _tprintf(_T ("thread got mutex ! \n") );
      break;
  5.   // time-out 발생하였다.
     case WAIT_TIMEOUT:
      _tprintf(_T ("timer expired ! \n") );
      return;
  6.   // 뮤텍스 반환이 적절이 이뤄지지 않았다.
     case WAIT_ABANDONED:
      return;
     }
  7.  for(DWORD i=0; i<5; i++)
     {
      _tprintf( _T("Thread Running ! \n") );
      Sleep(2000);
     }
  8.  ReleaseMutex(hMutex);
    }

  9. int _tmain(int argc, TCHAR* argv[])
    {
  10. #if 0
     hMutex = CreateMutex(
      NULL,                      
      FALSE,                    
      _T("NamedMutex")  
      );        
    #else
  11.  hMutex = OpenMutex(
      MUTEX_ALL_ACCESS,    
      FALSE,                
      _T("NamedMutex")  
      );
  12. #endif
  13.  if (hMutex == NULL)
     {
      _tprintf(_T("CreateMutex error: %d\n"), GetLastError());
      return -1;
     }
  14.  ProcessBaseCriticalSection();
  15.  CloseHandle(hMutex);
  16.  return 0;
    }

 

 

 

실행순서 동기화#

 

이벤트(Event) 기반의 동기화#

쓰레드나 커널 오브젝트의 경우 초기에는 Non-Signaled 상태로 생성되고, 종료시 Signaled 상태로 자동변경 된다. 그러나 이벤트 오브젝트는 자동으로 Signaled 상태로 변경되는것이 아니라 함수호출을 통해서 Signaled 상태로 변경을 해야한다. 따라서 프로그래머는 이벤트 커널 오브젝트의 상태를 직접 조작함으로써 실행 순서를 동기화 할 수 있다. 실행순서 동기화를 위한 내용을 정리하자면 아래와 같다.

 

  • A 쓰레드의 특정 시점까지 완료후 B 쓰레드가 시작되도록 하는 프로그램을 작성 하고자 한다.
  • A, B 쓰레드를 차례대로 생성하고, A 쓰레드는 Non-Signaled 상태로 이벤트 커널 오브젝트를 생성한다.
  • B 쓰레드 쓰레드 시작 부분에 WaitForSingleObject(hEvent,INFINITE) 함수를 호출하여 event 가 Signaled 상태가 되기를 기다린다.
  • 이벤트 오브젝트는 A 쓰레드에의해 Non-Signaled 상태이므로 B 쓰레드가 블로킹된다.
  • A 쓰레드는 SetEvent() 함수를 통해 이벤트 오브젝트의 상태를 Signaled 상태로 바꾸면 이때 블로킹되어있던 B 쓰레드가 블로킹 상태를 빠져나와 쓰레드가 실행된다.

 

  1.  // 이벤트 오브젝트 생성
  2. HANDLE CreateEvent(
  3. LPSECURITY_ATTRIBUTES lpEnventAttrivutes, // 보안속성 지정

  4. BOOL bManualReset, // 리셋모드 결정

    BOOL bInitialState, // 이벤트 오브젝트의 초기 상태결정 TRUE:Signaled, FALSE:Non-Signaled)

  5. LPCTSTR lpName // 오브젝트 이름

  6. );

  7. // Non-Signaled 상태로 변경
  8. BOOL ResetEvent(HANDLE hEvent);
  9. // Signaled 상태로 변경
  10. BOOL SetEvent(HANDLE hEvent);

 

  • 참고 : BOOL bManualReset

TRUE:수동리셋모드 - 블로킹된 쓰레드가 WiatForSingleObject 함수를 빠져나온후에도 이벤트의 상태가 변하지 않고 Signaled 상태로 남음

FALSE:자동리셋모드 - 블로킹된 쓰레드가 WiatForSingleObject 함수를 빠져나옴과 동시에 이벤트의 상태가 Non-Signaled 상태로 변함

 

 

예제)

  1. /*
    StringEvent.cpp
    프로그램 설명: 1. 생산자/소비자 모델의 이해
    2. 동기화 event에 대한 이해.
    */
  2. #include <stdio.h>
    #include <stdlib.h>
    #include <windows.h>
    #include <process.h>    /* _beginthreadex, _endthreadex */

  3. unsigned int WINAPI OutputThreadFunction(LPVOID lpParam);
  4. TCHAR string[100];
    HANDLE hEvent;
  5. int _tmain(int argc, TCHAR* argv[])
    {
     HANDLE  hThread;
     DWORD dwThreadID;
  6.  hEvent = CreateEvent( // event object 생성.
      NULL,  // 상속 불가.
      TRUE,  // manual-reset mode로 생성.
      FALSE,  // non-signaled 상태로 생성.
      NULL  // 이름 없는 event.
      );
     if(hEvent==NULL){
      _fputts(_T("Event object creation error \n"), stdout);
      return -1;
     }
  7.  hThread = (HANDLE)_beginthreadex (
      NULL, 0,
      OutputThreadFunction,
      NULL, 0,
      (unsigned *)&dwThreadID
      );
  8.  if(hThread==0) {
      _fputts(_T("Thread creation error \n"), stdout);
      return -1;
     }
  9.  _fputts(_T("Insert string: "), stdout);
     _fgetts(string, 30, stdin);
  10.  SetEvent(hEvent); // event의 state를 signaled 상태로 변경.
  11.  WaitForSingleObject(hThread, INFINITE);
  12.  CloseHandle(hEvent); // event 오브젝트 소멸
     CloseHandle(hThread);
  13.  return 0;
    }
  14. unsigned int WINAPI OutputThreadFunction(LPVOID lpParam)
    {
  15.  WaitForSingleObject(hEvent, INFINITE); // event가 signaled 상태가 되기를 기다린다.
  16.  _fputts(_T("output string: "), stdout);
     _fputts(string, stdout);
  17.  return 0;
  18. }

 

수동리셋(Muanual-Reset) 모드 이벤트 활용 예#

문자열입력, 문자열 출력, 문자열 길이를 출력하는 3개의 쓰레드가 있다.

  문자열 출력과 문자열 길이를 출력하는 쓰레드는 문자열이 입력되기를 기다리고 있다가 문자열의 입력이 완료되면  동시에 블로킹을 풀고 쓰레드가 실행되도록한다.

블로킹된 두개의 쓰레드를 동시에 풀기위해 Event 가 사용되고, 쓰레드가 동시에 실행되더라도 문자열 출력이 섞이지 않도록 하기위해 Mutex 가 사용된다.

 

  1.  /*
     StringEvent2.cpp
     프로그램 설명: manual-reset mode 동기화 적용 사례.
    */
  2. #include <stdio.h>
    #include <stdlib.h>
    #include <windows.h>
    #include <process.h>    /* _beginthreadex, _endthreadex */

  3. unsigned int WINAPI OutputThreadFunction(LPVOID lpParam);
    unsigned int WINAPI CountThreadFunction(LPVOID lpParam);
  4. TCHAR string[100];
    HANDLE hEvent;
  5. int _tmain(int argc, TCHAR* argv[])
    {
     HANDLE  hThread[2];
     DWORD dwThreadID[2];
  6.  hEvent = CreateEvent( // event object 생성.
        NULL,  // 상속 불가.
        TRUE,  // manual-reset mode로 생성.
        FALSE,  // non-signaled 상태로 생성.
        NULL  // 이름 없는 event.
        );
     if(hEvent==NULL){
      _fputts(_T("Event object creation error \n"), stdout);
      return -1;
     }
     
     hThread[0] = (HANDLE)_beginthreadex (
          NULL, 0,
          OutputThreadFunction,
          NULL, 0,
          (unsigned *)&dwThreadID[0]
         );
  7.  hThread[1] = (HANDLE)_beginthreadex (
          NULL, 0,
          CountThreadFunction,
          NULL, 0,
          (unsigned *)&dwThreadID[1]
         );

  8.  if(hThread[0]==0 ||hThread[1]==0)
     {
      _fputts(_T("Thread creation error \n"), stdout);
      return -1;
     }
  9.  _fputts(_T("Insert string: "), stdout);
     _fgetts(string, 30, stdin);
  10.  SetEvent(hEvent); // event의 state를 signaled 상태로 변경.
  11.  WaitForMultipleObjects (
        2,           // 배열의 길이.
        hThread,     // 핸들의 배열.
        TRUE,        // 모든 핸들이 신호받은 상태로 될 때 리턴.
        INFINITE  // 무한 대기.
     );
     
     CloseHandle(hEvent); // event 오브젝트 소멸
     CloseHandle(hThread[0]);
     CloseHandle(hThread[1]);
  12.     return 0;
    }
  13. unsigned int WINAPI OutputThreadFunction(LPVOID lpParam)
    {
  14.   WaitForSingleObject(hEvent, INFINITE); // event가 signaled 상태가 되기를 기다린다.
  15.   _fputts(_T("Output string: "), stdout);
      _fputts(string, stdout);
  16.   return 0;
    }
  17. unsigned int WINAPI CountThreadFunction(LPVOID lpParam)
    {
  18.   WaitForSingleObject(hEvent, INFINITE); // event가 signaled 상태가 되기를 기다린다.
  19.   _tprintf(_T("Output string length: %d \n"), _tcslen(string)-1);
  20.   return 0;
    }

 

위 코드의 문제점 : WaitForMultipleObjects() 함수를 통해 블로킹된 두개의 쓰레드가 동시에 풀리면서 출력순서를 보장하지 못한다.

따라서 위 코드를  Mutex 를 사용한 코드로 수정해야 한다.

 


  1. #include "stdafx.h"
  2. #include <stdio.h>
    #include <stdlib.h>
    #include <windows.h>
    #include <process.h>    /* _beginthreadex, _endthreadex */

  3. unsigned int WINAPI OutputThreadFunction(LPVOID lpParam);
    unsigned int WINAPI CountThreadFunction(LPVOID lpParam);
    DWORD AcquireMutex(HANDLE mutex);

  4. TCHAR string[100];
    HANDLE hEvent;
    HANDLE hMutex;

  5. int _tmain(int argc, TCHAR* argv[])
    {
     HANDLE  hThread[2];
     DWORD dwThreadID[2];
  6.  hMutex = CreateMutex( NULL, FALSE, _T("NamedMutex"));
     if(hMutex==NULL)
     {
      _fputts(_T("Mutex object creation error \n"), stdout);
      return -1;
     }
  7.  hEvent = CreateEvent( // event object 생성.
      NULL,  // 상속 불가.
      TRUE,  // manual-reset mode로 생성.
      FALSE,  // non-signaled 상태로 생성.
      NULL  // 이름 없는 event.
      );
  8.  if(hEvent==NULL)
     {
      _fputts(_T("Event object creation error \n"), stdout);
      return -1;
     }
  9.  hThread[0] = (HANDLE)_beginthreadex (
      NULL, 0,
      OutputThreadFunction,
      NULL, 0,
      (unsigned *)&dwThreadID[0]
      );
  10.  hThread[1] = (HANDLE)_beginthreadex (
      NULL, 0,
      CountThreadFunction,
      NULL, 0,
      (unsigned *)&dwThreadID[1]
      );
  11.  if(hThread[0]==0 ||hThread[1]==0)
     {
      _fputts(_T("Thread creation error \n"), stdout);
      return -1;
     }
  12.  _fputts(_T("Insert string: "), stdout);
     _fgetts(string, 30, stdin);
  13.  SetEvent(hEvent); // event의 state를 signaled 상태로 변경.
  14.  WaitForMultipleObjects (
      2,           // 배열의 길이.
      hThread,     // 핸들의 배열.
      TRUE,        // 모든 핸들이 신호받은 상태로 될 때 리턴.
      INFINITE  // 무한 대기.
      );
  15.  CloseHandle(hEvent); // event 오브젝트 소멸
     CloseHandle(hThread[0]);
     CloseHandle(hThread[1]);
  16.  return 0;
    }
  17. unsigned int WINAPI OutputThreadFunction(LPVOID lpParam)
    {
  18.  WaitForSingleObject(hEvent, INFINITE); // event가 signaled 상태가 되기를 기다린다.
  19.  AcquireMutex(hMutex);
  20.  _fputts(_T("Output string: "), stdout);
     _fputts(string, stdout);
  21.  ReleaseMutex(hMutex);
  22.  return 0;
    }
  23. unsigned int WINAPI CountThreadFunction(LPVOID lpParam)
    {
  24.  WaitForSingleObject(hEvent, INFINITE); // event가 signaled 상태가 되기를 기다린다.
  25.  AcquireMutex(hMutex);
  26.  _tprintf(_T("Output string length: %d \n"), _tcslen(string)-1);
  27.  ReleaseMutex(hMutex);
  28.  return 0;
    }
  29. DWORD AcquireMutex(HANDLE mutex)
    {
     return WaitForSingleObject(mutex, INFINITE);
    }

 

 

타이머 기반 동기화#

Windows 에서는 Signaled/Non-Signaled 상태 개념이 중요하다. 어떤 커널 오브젝트는 특정 상황이 도래하면 Signaled 상태가 되고, 또 어떤 커널 오브젝트는 명시적인 함수 호출을 통해서 Signaled 상태가 되기도 한다.

타이머는 생성시 Non-Signaled 상태로 생성되고 정해진 시간이 지나면 자동으로 Signaled 상태가되는 특성이 있다.

 

  • 타이머 오브젝트 생성

HANDLE CreateWaitableTimer(

LPSECURITY_ATTRIBUTES lpTimerAttributes, //  보안속성 지정

BOOL bManualReset, // 수동리셋/자동리셋 모드 설정

LPCTSTR lpTimerName // 타이머 오브텍트 이름 설정

);

 

  •  알람 시간 설정

BOOL SetWaitableTimer(

HANDLE hTimer, // 알람을 설정할 타이머 오브젝트 핸들

const LARGE_INTEGER* pDueTime, // 알람이 울리는 시간 지정, 단위는 NanoSeconds (+값일경우 절대시간, -값일경우 상대시간을 의미)

LONG lPeriod, // 타이머의 주기, 단위는 Milliseconds

PTIMERAPCROUTINE pfnCompletetionRoutine, // 완료루틴 타이머 생성 용도로 사용

LPVOID lpArgToCompletionRoutine, // 완료루틴 타이머 생성 용도로 사용

BOOL fResume // 전원관리와 관련된 매개변수, 기본적으로 FALSE 로 세팅함

);

 

  •  타이머 해제

BOOL CancelWaitableTimer(

HANDLE hTimer // 알람을 해제할 타이머 오브젝트의 핸들

);

 

수동리셋 타이머(Manual-Reset Timer)#

 

예제)

  1.  /*
     ManualResetTimer.cpp
     프로그램 설명: 수동 리셋 타이머 오브젝트에 대한 이해.
    */
  2. #define _WIN32_WINNT 0x0400
  3. #include <stdio.h>
    #include <stdlib.h>
    #include <windows.h>

  4. int _tmain(int argc, TCHAR* argv[])
    {
        HANDLE hTimer = NULL;
        LARGE_INTEGER liDueTime;
  5.     liDueTime.QuadPart=-100000000;
  6.     hTimer = CreateWaitableTimer(NULL, TRUE, _T("WaitableTimer")); // 수동리셋 타이머 생성
        if (!hTimer)
        {
            _tprintf( _T("CreateWaitableTimer failed (%d)\n"), GetLastError());
            return 1;
        }
  7.     _tprintf( _T("Waiting for 10 seconds...\n"));
  8.     SetWaitableTimer(hTimer, &liDueTime, 0, NULL, NULL, FALSE); // 타이머 시간 설정
  9.     WaitForSingleObject(hTimer, INFINITE); // 수동리셋 타이머 이므로 타이머가 특정시간이 지난후 Signaled 상태로 변경되면 쓰레드가 블로킹에서 풀려나지만 타이머의 상태는 계속 Signaled 상태로 유지된다.

  10.     _tprintf( _T("Timer was signaled.\n") );
        MessageBeep(MB_ICONEXCLAMATION);
  11.     return 0;
    }

 

#define _WIN32_WINNT   0x0400

CreateWaitableTimer, SetWaitableTimer 은 Windows NT 4.0 이후부터 제공되기 시작한 함수이기때문에 매크로 선언을 미리 해 줘야 사용 가능함.

만약 Widows XP 이후 버전부터 제공하기 시작한 함수가 있다면 _WINNT_WIN32 를 0x0501 로 정의해야한다.

 

 

 

주기적 타이머(Periodic-Timer)#

일정 시간 간격으로 알람이 울리는 타이머.

 

  1.  /*
     PeriodicTimer.cpp
     프로그램 설명: 주기적 타이머에 대한 이해.
    */
  2. #define _WIN32_WINNT 0x0400
  3. #include <stdio.h>
    #include <stdlib.h>
    #include <windows.h>

  4. int _tmain(int argc, TCHAR* argv[])
    {
        HANDLE hTimer = NULL;
        LARGE_INTEGER liDueTime;
  5.     liDueTime.QuadPart=-100000000;
  6.     hTimer = CreateWaitableTimer(NULL, FALSE, _T("WaitableTimer")); // 자동리셋 타이머 생성
        if (!hTimer)
        {
            _tprintf( _T("CreateWaitableTimer failed (%d)\n"), GetLastError());
            return 1;
        }
  7.     _tprintf( _T("Waiting for 10 seconds...\n"));
  8.     SetWaitableTimer(hTimer, &liDueTime, 5000, NULL, NULL, FALSE); // 타이머 주기 설정
  9.     while(1)
       {
          WaitForSingleObject(hTimer, INFINITE); // 자동리셋 타이머 이므로 타이머가 Signaled 상태로 변경됨과 동시에 쓰레드가 블로킹 상태에서 빠져나오고 타이머의 상태는 Non-Signaled 상태로 바뀐다.
          _tprintf( _T("Timer was signaled.\n") );
          MessageBeep(MB_ICONEXCLAMATION);
       }
        return 0;
    }

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

댓글 없음:

댓글 쓰기