Event, Thread

Program Visual C++ 2011. 8. 25. 10:33 Posted by HisPark


이벤트는 특정 사건의 발생을 다른 쓰레드에 알리는 경우에 주로 사용한다.

이벤트를 이용한 동기화는 다음과 같은 과정으로 진행된다.

이벤트 A를 비신호 상태로생성

한 쓰레드가 작업을 진행하고, 나머지 쓰레드는 이벤트 A가 신호 상태가 될때까지 대기

쓰레드가 작업을 완료하면 이벤트 A를 신호상태로 변경

나머지 쓰레드 작업 시작

기본적으로 이벤트는 신호상태일때 접근 가능하고, 비신호상태일때 접근 불가능하다.

이벤트 생성함수

CreateEvent (SECURITY_ATTRIBUTES (대부분 NULL로 사용),

bManualReset (TRUE 이면 수동리셋 이벤트,FALSE 이면 자동리셋 이벤트생성),

bInitialState (TRUE면 신호상태로,FALSE면 비신호 상태로 시작),

lpName (이벤트를 서로다른 프로세스에 속한 쓰레드가 사용할수 있도록

이름을 줄수 있다. NULL을 사용하면 이름없는 이벤트가 생성)

)

자동리셋이벤트: 이벤트를 신호상태로 바꾸면 기다리는 쓰레드 중 하나만 꺠운후

자동으로 비신호 상태가 된다. 따라서 자동리셋 이벤트에 대해서는

ResetEvent 함수를 사용할 필요가 없다.

수동리셋이벤트: 이벤트를 신호상태로 바꾸면 계속 신호상태를 유지하므로 결과적으로

대기중인 쓰레드를 모두 깨우게 된다. 이 경우 비신호 상태로 바꾸려면

ResetEvent 함수를 사용해야 한다.

상태변경함수

BOOL SetEvent(이벤트 핸들) // 비신호->신호

BOOL ResetEvent(이벤트 핸들) // 신호->비신호

이벤트 예제

하나의 쓰레드가 버퍼에 데이터를 쓰고 나머지 쓰레드에서 출력하는 예제

#define BUFSIZE 16

HANDLE hReadEvent;

HANDLE hWriteEvent;

int buf[BUFSIZE];

DWORD WINAPI WriteThread(LPVOID arg)

{

DWORD retval;

for (int k=0; k<10; k++)

{

retval = WaitForSingleObject(hReadEvent,INFINITE); //Read이벤트 끝날때 까지 대기

if (retval == WAIT_FAILED) break;

for(int i=0; i<BUFSIZE; i++)

buf[i] = i;

SetEvent(hWriteEvent); //Write이벤트 신호 상태로 변경

}

CloseHandle(hWriteEvent); //이벤트 제거

return 0;

}

DWORD WINAPI ReadThread(LPVOID arg)

{

DWORD retval;

while(1)

{

retval=WaitForSingleObject(hWriteEvent,INFINITE); //Write 이벤트 끝날때 까지 대기

if (retval == WAIT_FAILED) break;

cout << "Thread ID " << GetCurrentThreadId() << ":\t";

for(int i=0; i<BUFSIZE; i++)

cout << buf[i] << " ";

cout << "\n";

ZeroMemory(buf,sizeof(buf)); //버퍼 초기화

SetEvent(hReadEvent); //Read이벤트 신호 상태로 변경

}

return 0;

}

void main()

{

/*

둘다 자동리셋 이벤트로 시작

대기중인 쓰레드중 하나의 쓰레드만 이벤트의 제어권을 넘겨 받고 자동으로

비신호 상태가 된다

*/

hReadEvent = CreateEvent(NULL,FALSE,TRUE,NULL); //신호상태 시작

if (hReadEvent == NULL) return;
hWriteEvent=CreateEvent(NULL,FALSE,FALSE,NULL); //비신호상태 시작
if (hWriteEvent == NULL) return;

HANDLE hThread[3];
DWORD ThreadId[3];

hThread[0] = CreateThread(NULL,0,WriteThread,NULL,0,&ThreadId[0]);
hThread[1] = CreateThread(NULL,0,ReadThread,NULL,0,&ThreadId[1]);
hThread[2] = CreateThread(NULL,0,ReadThread,NULL,0,&ThreadId[2]);

WaitForMultipleObjects(3,hThread,TRUE,INFINITE);

CloseHandle(hReadEvent);
cout << "모든작업을 완료했습니다..!!\n";

}

Wirte이벤트 와 Read이벤트가 서로 동기화를 하고 있고

같은 Read이벤트를 제어하는 1번과 2번 쓰레드가 한번 더 동기화를 하고 있다.

Read이벤트는 신호상태로 Write 이벤트는 비신호 상태로 시작하였다.

Write 이벤트가 비신호 상태로 시작하였기 때문에 Read쓰레드 에서 Write 이벤트가

끝나기 전까지 무한정 대기하게 된다.

Write 쓰레드의 루프가 한번 돌면 Write이벤트가 신호상태가 되면서

대기중이던 Read 쓰레드의 코드가 실행된다. Read이벤트는 자동 리셋 이벤트로

생성되었으므로 제어권이 1쓰레드에 넘어갔을때는 자동적으로 비신호 상태가 된다.

1번쓰레드->2번쓰레드 순으로 코드 를 실행한후 Read이벤트가 신호상태가 되면서

다시 Write 쓰레드의 루프가 돌아가는 방식이다.

Write 쓰레드에서 동기화를 시키지 않았다면?

Write 이벤트는 비신호 상태로 시작하였다. 때문에 Read 쓰레드에서 무한정 대기하게 되며

상태가 바뀌지 않으므로 무한 대기상태에 빠지게 된다.

Read 쓰레드에서 동기화를 시키지 않았다면?

SetEvent 로 신호상태로 전환해 주지 않으면 1번쓰레드에 있는

제어권이 2번쓰레드로 넘어가지 않을것 이다. 또한 신호상태가 되지 않았기 때문에

Write 쓰레드에서도 무한정 대기하게 된다. 결과적으로 1번쓰레드에 있는 출력코드가

1번 실행대고 무한 대기상태에 빠지게 된다.

[출처] 쓰레드 이벤트|작성자 로지어

'Program Visual C++' 카테고리의 다른 글

AVI 파일 다루기  (0) 2011.08.30
정적 라이브러리에서 MFC 사용  (0) 2011.08.25
MFC 더블버퍼링 메모리 버퍼 이용  (0) 2011.08.25
RAW Data Draw  (0) 2011.08.11
YUV to RGB ...  (0) 2011.08.10