[네트워크C++ - 동기화]쓰레드 동기화(실행순서 동기화) -이벤트,타이머  쓰레드 / [네트워크기초]

2008/01/17 21:54

복사http://blog.naver.com/blue7red/100046389559

1.순서동기화의 이유

 

-생산자/소비자 모델

외부-->입력 -->쓰레드1(생산자쓰레드) -->버퍼(20메가)-->쓰레드2(소비자쓰레드) -->출력

 

출력은 입력에 의존적이다.

입력은 외부환경에 의존적이다.

 

출력하고 있는데 갑자기 엄청난 입력(10메가)이 들어온다면 데이터의 손실이 발생할 수 있기 때문에 위와 같이 구성한다.

 

버퍼가 비워있는 상태에서 생산자가 데이터를 날라주기전에 소비자가 버퍼에서 가져오면 쓰레기값이 가져오게된다.

 

2.이벤트 기반의 동기화

 

-생산자가 데이터를 가져다 놓았다는 것은 생산자만이 알 수 있다.(결정대상은 프로그래머가 결정한다)

-소비자는 WaitForSingleObject()로 대기중(블로킹상태)

-생산자가 데이터를 가져다 놓으면 시그널상태로 커널오브젝트를 바꾼다.(SetEvent)

-이제야 소비자는 데이터를 가져온다.

 

CreateEvent()로 이벤트객체 생성.

시그널드 : 대기하고 있는 쓰레드가 이벤트객체를 획득할 수 있는 상태

넌그시널드:대기하고 있는 쓰레드가 이벤트 객체를 획득할 수 없는 상태

 

 

(1)수동 리셋모드 이벤트

넌시그널드 ------SetEvent-------------------> 시그널드 ("데이터 가져다 놓았다 소비자야.일해!!") ( 수동)

넌시그널드 <-----ResetEvent------------------시그널드  ("소비자야 이제 대기해") (수동)

 

-수동으로 다 해줘야된다.

-소비자:WaitForSingleObject로 대기중

만약에 두 개 이상의 소비자쓰레드가 존재한다고 할 때 수동모드에서는 시그널상태가 되면 WaitForSingleObject에서

대기중인 두 개 이상의 소비자쓰레드가 WaitForSingleObject를 빠져나와서 동시에 깨어나게 된다.

 

 

-관찰중인 두 쓰레드가 동시에 깨어나야 할때

 

(2)자동 리셋모드 이벤트

넌시그널드 ------SetEvent-------------------> 시그널드            (수동)

넌시그널드 <-----WaitForSingleObject------------------시그널드 (자동)

-시그널드를 주는 것은 수동이다.SetEvent() - 생산자쓰레드만 알기때문에

-넌시그널드- 시그널드로 바뀌는 것은 자동이다.

 

-한 번에 하나의 쓰레드만 깨어나게 할 때.

 

둘중 하나만실행의 기회를 얻게 된다.

 

3.이벤트 예제 소스

 

쓰레드 -소비자의 역할

메인쓰레드 - 생산자쓰레드의 역할

 

출처:윤성우  -윈도우즈시스템프로그래밍

/*
 StringEvent.cpp
 프로그램 설명: 1. 생산자/소비자 모델의 이해
                2. 동기화 event에 대한 이해.
*/

#include <stdio.h>
#include <stdlib.h>
#include <windows.h>
#include <process.h>    /* _beginthreadex, _endthreadex */


unsigned int WINAPI OutputThreadFunction(LPVOID lpParam);

TCHAR string[100];
HANDLE hEvent;

int _tmain(int argc, TCHAR* argv[])

 HANDLE  hThread;
 DWORD dwThreadID;

 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 = (HANDLE)_beginthreadex (
      NULL, 0, 
      OutputThreadFunction, 
      NULL, 0, 
      (unsigned *)&dwThreadID
     );

 if(hThread==0) {
  _fputts(_T("Thread creation error \n"), stdout); 
  return -1;
 } 

 _fputts(_T("Insert string: "), stdout); 
 _fgetts(string, 30, stdin);

 SetEvent(hEvent); // event의 state를 signaled 상태로 변경.

 WaitForSingleObject(hThread, INFINITE);
  
  CloseHandle(hEvent); // event 오브젝트 소멸
 CloseHandle(hThread);

    return 0;
}

unsigned int WINAPI OutputThreadFunction(LPVOID lpParam)
{

  WaitForSingleObject(hEvent, INFINITE); // event가 signaled 상태가 되기를 기다린다.

  _fputts(_T("output string: "), stdout); 
  _fputts(string, stdout);

  return 0;
}

 

 

/*
 StringEvent3.cpp
 프로그램 설명: event, mutex 동시 사용 사례.
*/

#include <stdio.h>
#include <stdlib.h>
#include <windows.h>
#include <process.h>    /* _beginthreadex, _endthreadex */


unsigned int WINAPI OutputThreadFunction(LPVOID lpParam);
unsigned int WINAPI CountThreadFunction(LPVOID lpParam);

typedef struct _SynchString
{
 TCHAR string[100];
 HANDLE hEvent;
 HANDLE hMutex; 
} SynchString;

SynchString gSynString;

int _tmain(int argc, TCHAR* argv[])

 HANDLE  hThreads[2];
 DWORD dwThreadIDs[2];

 gSynString.hEvent = CreateEvent( 
       NULL,  
       TRUE,  
       FALSE,  
       NULL  
      ); 

 gSynString.hMutex = CreateMutex ( 
       NULL,
       FALSE,
       NULL
      );   

 if(gSynString.hEvent==NULL || gSynString.hMutex==NULL) {
  _fputts(_T("kernel object creation error \n"), stdout); 
  return -1;
 }


 hThreads[0] = (HANDLE)_beginthreadex (
      NULL, 0, 
      OutputThreadFunction, 
      NULL, 0, 
      (unsigned *)&dwThreadIDs[0]
     );

 hThreads[1] = (HANDLE)_beginthreadex (
      NULL, 0, 
      CountThreadFunction, 
      NULL, 0, 
      (unsigned *)&dwThreadIDs[1]
     );


 if(hThreads[0]==0 ||hThreads[1]==0) 
 {
  _fputts(_T("Thread creation error \n"), stdout); 
  return -1;
 } 

 _fputts(_T("Insert string: "), stdout); 
 _fgetts(gSynString.string, 30, stdin);

 SetEvent(gSynString.hEvent); // event의 state를 signaled 상태로 변경.

 WaitForMultipleObjects ( 
    2,           // 배열의 길이.
    hThreads,     // 핸들의 배열.
    TRUE,        // 모든 핸들이 신호받은 상태로 될 때 리턴.
    INFINITE  // 무한 대기.
 ); 
  
  CloseHandle(gSynString.hEvent);
  CloseHandle(gSynString.hMutex);
 CloseHandle(hThreads[0]);
 CloseHandle(hThreads[1]);

    return 0;
}

unsigned int WINAPI OutputThreadFunction(LPVOID lpParam)
{

  WaitForSingleObject(gSynString.hEvent, INFINITE); // event가 signaled 상태가 되기를 기다린다.
  WaitForSingleObject(gSynString.hMutex, INFINITE);

  _fputts(_T("Output string: "), stdout); 
  _fputts(gSynString.string, stdout);

  ReleaseMutex(gSynString.hMutex);

  return 0;
}

unsigned int WINAPI CountThreadFunction(LPVOID lpParam)
{

  WaitForSingleObject(gSynString.hEvent, INFINITE); // event가 signaled 상태가 되기를 기다린다.
  WaitForSingleObject(gSynString.hMutex, INFINITE);

  _tprintf(_T("Output string length: %d \n"), _tcslen(gSynString.string)-1);

  ReleaseMutex(gSynString.hMutex);

  return 0;
}

 

4.수동/자동 리셋 타이머

 

(1)타이머 생성

HANDLE CreateWaitableTimer

(LPSECURITY_ATTRIBUTES lpAtrri,

BOOL bManual,                                //모드 설정 (수동이면 최초 한번만 실행, 자동이면 최초 실행+ 일정간격반복)

LPCTSTR lpName

);

 

(2)타이머 설정

BOOL SetWaitableTimer

(HANDLE hTimer,

const LARGE_INTEGER* pDueTime, //최초 몇 초 뒤에(처음 실행에 대한 설정) 

LONG lPeriod, //반복은 몇 초 간격으로 (반복에 대한 설정) - 여기가 0이면 한번만 실행하고 종료한다.

PTIMERAPCROUTINE pfnCompletionRoutine, //실행할 함수

LPVOID  lpArgToCompletionRoutine,             //실행할 함수의 전달인자

BOOL fResume,                                         //전원관리와 관련있는 매개변수(일반적으로 FALSE를 쓰자)

);

//설정사항

-몇 초 뒤에

-반복

 

수동 리셋 타이머 : 일정 시간후 특정 함수 호출

자동 리셋 타이머 : 수동 + 반복

 

 

5.타이머 예제소스

 

(1)수동리셋타이머

/*
 ManualResetTimer.cpp
 프로그램 설명: 수동 리셋 타이머 오브젝트에 대한 이해.
*/

#define _WIN32_WINNT 0x0400

#include <stdio.h>
#include <stdlib.h>
#include <windows.h>


int _tmain(int argc, TCHAR* argv[])
{
    HANDLE hTimer = NULL;
    LARGE_INTEGER liDueTime;

    liDueTime.QuadPart=-100000000;//나노세컨드단위로 설정(현재 시점을 기준으로 10초 후에)

    hTimer = CreateWaitableTimer(NULL, TRUE, _T("WaitableTimer"));
    if (!hTimer)
    {
        _tprintf( _T("CreateWaitableTimer failed (%d)\n"), GetLastError());
        return 1;
    }

    _tprintf( _T("Waiting for 10 seconds...\n"));

    SetWaitableTimer(hTimer, &liDueTime0, NULL, NULL, FALSE); //0이므로 반복하지 않는다.

     WaitForSingleObject(hTimer, INFINITE);
    _tprintf( _T("Timer was signaled.\n") );
    MessageBeep(MB_ICONEXCLAMATION);

    return 0;
}

 

(2)자동리셋타이머

/*
 PeriodicTimer.cpp
 프로그램 설명: 주기적 타이머에 대한 이해.
*/

#define _WIN32_WINNT 0x0400

#include <stdio.h>
#include <stdlib.h>
#include <windows.h>


int _tmain(int argc, TCHAR* argv[])
{
    HANDLE hTimer = NULL;
    LARGE_INTEGER liDueTime;

    liDueTime.QuadPart=-100000000;

    hTimer = CreateWaitableTimer(NULL, FALSE, _T("WaitableTimer"));//자동리셋타이머
    if (!hTimer)
    {
        _tprintf( _T("CreateWaitableTimer failed (%d)\n"), GetLastError());
        return 1;
    }

    _tprintf( _T("Waiting for 10 seconds...\n"));

    SetWaitableTimer(hTimer, &liDueTime, 5000, NULL, NULL, FALSE); //5초마다 반복

    while(1)
   {
      WaitForSingleObject(hTimer, INFINITE);
      _tprintf( _T("Timer was signaled.\n") );
      MessageBeep(MB_ICONEXCLAMATION);
   }
    return 0;
}

 

6.참고자료

SetWaitableTimer

The SetWaitableTimer function activates the specified waitable timer. When the due time arrives, the timer is signaled and the thread that set the timer calls the optional completion routine.

BOOL SetWaitableTimer(
  HANDLE hTimer,
  const LARGE_INTEGER* pDueTime,
  LONG lPeriod,
  PTIMERAPCROUTINE pfnCompletionRoutine,
  LPVOID lpArgToCompletionRoutine,
  BOOL fResume
);

Parameters

hTimer
[in] Handle to the timer object. The CreateWaitableTimer or OpenWaitableTimer function returns this handle.

The handle must have the TIMER_MODIFY_STATE access right. For more information, see Synchronization Object Security and Access Rights.

pDueTime
[in] Time after which the state of the timer is to be set to signaled, in 100 nanosecond intervals. Use the format described by theFILETIME structure. Positive values indicate absolute time. Be sure to use a UTC-based absolute time, as the system uses UTC-based time internally. Negative values indicate relative time. The actual timer accuracy depends on the capability of your hardware. For more information about UTC-based time, see System Time.
lPeriod
[in] Period of the timer, in milliseconds. If lPeriod is zero, the timer is signaled once. If lPeriod is greater than zero, the timer is periodic. A periodic timer automatically reactivates each time the period elapses, until the timer is canceled using theCancelWaitableTimer function or reset using SetWaitableTimer. If lPeriod is less than zero, the function fails.
pfnCompletionRoutine
[in] Pointer to an optional completion routine. The completion routine is application-defined function of type PTIMERAPCROUTINE to be executed when the timer is signaled. For more information on the timer callback function, see TimerAPCProc.
lpArgToCompletionRoutine
[in] Pointer to a structure that is passed to the completion routine.
fResume
[in] If this parameter is TRUE, restores a system in suspended power conservation mode when the timer state is set to signaled. Otherwise, the system is not restored. If the system does not support a restore, the call succeeds, but GetLastError returns ERROR_NOT_SUPPORTED.

Return Values

If the function succeeds, the return value is nonzero.

If the function fails, the return value is zero. To get extended error information, call GetLastError.

Remarks

Timers are initially inactive. To activate a timer, call SetWaitableTimer. If the timer is already active when you callSetWaitableTimer, the timer is stopped, then it is reactivated. Stopping the timer in this manner does not set the timer state to signaled, so threads blocked in a wait operation on the timer remain blocked.

When the specified due time arrives, the timer becomes inactive and the APC is queued to the thread that set the timer. The state of the timer is set to signaled, the timer is reactivated using the specified period, and the thread that set the timer calls the completion routine when it enters an alertable wait state. For more information, see QueueUserAPC.

If the thread that set the timer exits before the timer elapses, the timer is cancelled. If you call SetWaitableTimer on a timer that has been set by another thread and that thread is not in an alertable state, the completion routine is cancelled.

When a manual-reset timer is set to the signaled state, it remains in this state until SetWaitableTimer is called to reset the timer. As a result, a periodic manual-reset timer is set to the signaled state when the initial due time arrives and remains signaled until it is reset. When a synchronization timer is set to the signaled state, it remains in this state until a thread completes a wait operation on the timer object.

 

 

TimerAPCProc

The TimerAPCProc function is an application-defined timer completion routine. Specify this address when calling theSetWaitableTimer function. The PTIMERAPCROUTINE type defines a pointer to this callback function. TimerAPCProc is a placeholder for the application-defined function name.

VOID CALLBACK TimerAPCProc(
  LPVOID lpArgToCompletionRoutine,
  DWORD dwTimerLowValue,
  DWORD dwTimerHighValue
);

Parameters

lpArgToCompletionRoutine
[in] Value passed to the function using the lpArgToCompletionRoutine parameter of the SetWaitableTimer function.
dwTimerLowValue
[in] Low-order portion of the UTC-based time at which the timer was signaled. This value corresponds to the dwLowDateTimemember of the FILETIME structure. For more information about UTC-based time, see System Time.
dwTimerHighValue
[in] High-order portion of the UTC-based time at which the timer was signaled. This value corresponds to the dwHighDateTimemember of the FILETIME structure.

Return Values

This function does not return a value.

Remarks

The completion routine is executed by the thread that activates the timer using SetWaitableTimer. However, the thread must be in an alertable state. For more information, see Asynchronous Procedure Calls.

반응형

+ Recent posts