=================================
=================================
=================================
출처: http://artisticbit.tistory.com/entry/WaitForSingleObject-%ED%95%A8%EC%88%98
MSDN
쓰레드 동기화 시키는 방법에는 유저모드와 커널모드로 나눠지지만 WaitForSingleObject함수를 사용하는 경우는
출처: http://artisticbit.tistory.com/entry/WaitForSingleObject-함수 [끄적끄적]
=================================
=================================
=================================
출처: http://winnerz.tistory.com/entry/WaitForSingleObject-%EC%82%AC%EC%9A%A9%EC%8B%9C-Thread-%EC%A2%85%EB%A3%8C%EB%90%98%EC%97%88%EB%8A%94%EC%A7%80-%EC%A3%BC%EC%9D%98%ED%95%A0-%EA%B2%83
Thread 의 종료를 기다리기 WaitForSingleObject 를 사용할때 주의할 사항이 있다.
[main]
SetEvent(exit_event);
DWORD waitResult;
waitResult = WaitForSingleObject(m_loadingThread, 20000);[m_loadingThread]
while(true) {
waitResult = WaitForSingleObject(exit_event, 20000);
if (waitResult == WAIT_OBJECT_0) break;
// Some Task that takes some time.
}
위의 코드를 보자. m_loadingThread 에는 exit_event 를 주면 Thread 가 종료되는 코드가 루프로 돌고 있고, main 에서는 exit_event 를 세팅하여 Thread 를 종료시키고자 한다. 하지만, 이 경우 문제점이 있다.
Thread 가 "Some Task" 를 실행중인 경우라면 WaitForSingleObject 문이 실행되는 시점에 Thread 가 살아있고, Thread 가 루프를 다시 돌아 break 를 만나 쓰레드가 종료되면 해피한 케이스로 모든 일이 종료될 것이다.
하지만, SetEvent 와 동시에 break 를 만난다면? SetEvent 와 동시에 쓰레드 루프에서 벗어나 쓰레드 함수가 종료될 것이고, WaitForSingleObject 는 이미 종료되어 버린 Thread 의 handle 을 무작정 기다리게 된다.
MSDN 에서는 "어떤 현상이 발생할지 알 수 없다." 고 경고하고 있다. (빨간색 부분)
(http://msdn.microsoft.com/en-us/library/ms687032(VS.85).aspx)
The WaitForSingleObject function returns when the specified object is in the signaled state or the time-out interval elapses.
To enter an alertable wait state, use the WaitForSingleObjectEx function. To wait for multiple objects, use the WaitForMultipleObjects.
DWORD WaitForSingleObject( HANDLE hHandle, DWORD dwMilliseconds );
Parameters
- hHandle
- [in] Handle to the object. For a list of the object types whose handles can be specified, see the following Remarks section.
If this handle is closed while the wait is still pending, the function's behavior is undefined.
The handle must have the SYNCHRONIZE access right. For more information, see Standard Access Rights.
- dwMilliseconds
- [in] Time-out interval, in milliseconds. The function returns if the interval elapses, even if the object's state is nonsignaled. If dwMilliseconds is zero, the function tests the object's state and returns immediately. If dwMilliseconds is INFINITE, the function's time-out interval never elapses.
실제 테스트 결과, 대체로 Hangup 이 걸리는 현상이 발생하였다.
사용시 Thread 가 종료된 상태로 WaitForSingleObject 를 만나는 일이 없도록 주의해야 한다.
Thread 종료시 Sleep 을 이용하는 방법도 있겠지만 Sleep 을 쓰는 방법은 가급적 지양하는 것이 좋을 것이므로
그래서 아래와 같이 로직을 보완하였다. thread_state 를 두어 Thread가 종료되었는지 체크하도록 한다.
[main]
SetEvent(exit_event);
DWORD waitResult;if (thread_state != MY_STATE_EXIT)
waitResult = WaitForSingleObject(m_loadingThread, 20000);[m_loadingThread]
thread_state = MY_STATE_RUNNING;
while(true) {
waitResult = WaitForSingleObject(exit_event, 20000);
if (waitResult == WAIT_OBJECT_0) break;
// Some Task that takes some time.
}
thread_state = MY_STATE_EXIT
출처 : http://blog.naver.com/hankawiii?Redirect=Log&logNo=90084211565
출처: http://winnerz.tistory.com/entry/WaitForSingleObject-사용시-Thread-종료되었는지-주의할-것 [조...좋은 개발자다!]
=================================
=================================
=================================
출처: http://teraphonia.tistory.com/19
다음은 소스코드.
//----------------------------------------------
#pragma comment(lib, "Ws2_32.lib")
#include <winsock2.h>
#include <Windows.h>
#include <process.h>
#include <stdio.h>
unsigned __stdcall handle(void *pData)
{
printf("thread 실행중\n");
Sleep(3000);
return 0;
}
void main()
{
HANDLE hThread;
DWORD dwThreadID;
DWORD dw;
hThread = (HANDLE)::_beginthreadex(nullptr, 0, handle, nullptr, 0, (unsigned*)&dwThreadID);
if(hThread == 0)
{
puts("_beginthreadex Error");
return;
}
dw = WaitForSingleObject(hThread, INFINITE); //
if(dw == WAIT_FAILED)
{
puts("thread error");
return;
}
printf("main 종료\n");
printf("thread 종료\n");
}
//--------------------------------------------
handle 함수에서 자식 스레드를 생성해서 돌리는데,
만약 main이 먼저 종료된다면.. main은 부모 스레드이기 때문에 자식 스레드도 자연히 소멸된다.
여기서 만약 WaitForSingleObject를 쓰지 않는다면, main이 먼저 종료되어 거의 실행 직후에 바로 종료가 될 것이다.
하지만 WaitForSingleObject를 사용하면 해당 스레드(hThread 핸들이 가리키는 스레드)가 2번째 인자로 들어오는 시간만큼 지연시켜 준다. 즉, 위 코드에서는 Sleep(3000)으로 인해 3초간 대기하게 되는 스레드가 생기는데, WaitForSingleObject의 2번째 인자를 2000으로 넣는다면, main스레드에서는 이순간부터 2초간 그 스레드가 Signal 상태가 되기를 기다린다(main 스레드는 정지된다). 2초가 지나면 다시 main스레드도 실행이 되기 시작한다. 2번째 인자를 INFINITE로 넣는다면, 해당 스레드가 완전히 종료될 때까지(Signal 상태가 될 때까지) main 스레드는 대기한다.
출처: http://teraphonia.tistory.com/19 [Teraphonia]
=================================
=================================
=================================
출처: http://hashs.tistory.com/208
모든 윈도우에서 사용되는 리소스는 커널오브젝트라는 것을 가진다.
이 커널 오브젝트는 2가지 상태값을 가지게 되는데, 시그널 상태와 논시그널 상태이다.
CreateEvent함수는 개체를 시그널 or 논시그널로 핸들을 생성할 수 있다.
그리고 WaitForSingleObject함수로 CreateEvent에서 생성한 핸들이 시그널이 될때까지 기다리게 할 수 있다.
SetEvent는 핸들의 시그널 값을 논시그널에서 시그널로 바꿔주는 역할을 한다.
예제>
HANDLE WowEvent;
DWORD WINAPI ThreadProc(LPVOID lpParam)
{
Sleep(5000);
SetEvent( WowEvent ); // 3) 5초후 WowEvent 시그널 상태로 변경한다.
return 0;
}
int _tmain(int argc, _TCHAR* argv[])
{
DWORD ID = 0;
CreateThread(NULL, 0, ThreadProc, NULL, 0, &ID);
WowEvent = CreateEvent(0, FALSE, FALSE, 0); // 1) WowEvent를 논시그널 상태로 생성한다.
WaitForSingleObject(WowEvent, INFINITE); // 2) WowEvent가 시그널 상태로 되기를 기다린다.
// 4) WaitForSingleObject를 빠져나온다.
return 0;
}
출처: http://hashs.tistory.com/208 [V l i n k !]
=================================
=================================
=================================
출처: http://adolys.tistory.com/entry/%EC%8A%A4%EB%A0%88%EB%93%9C-%EC%9D%B4%EB%B2%A4%ED%8A%B8Thread-Event
* 이벤트는
특정 사건의 발생을 다른 스레드에 알리는 경우
에 주로 사용.
* 이벤트를 이용한 스레드 동기화 과정
- 이벤트 A 비신호 상태로 생성 → 하나의 스레드가 작업을 진행 → 나머지 스레드는 이벤트 A가 신호 상태가 될때까지 대기
→ 작업을 진행하던 스레드가 완료되면 이벤트 A를 신호 상태로 변경 → 나머지 스레드 작업 시작
* 이벤트는 신호상태일때 접근 가능, 비신호 상태일때는 접근 불가능
* 이벤트 생성함수
- CreateEvent( SECURITY_ATTRIBUTES, (대부분
NULL로 사용)
bMaunalReset, (
TRUE이면 수동리셋,
FALSE이면 자동리셋이벤트 생성)
bInitialState, (
TRUE이면 신호 상태,
FALSE이면 비신호 상태로 시작)
lpName (이벤트를 서로 다른 프로세스에 속한 스레드가 사용할 수 있도록
이름을 줄 수 있다. NULL을 사용하면 이름없는 이벤트 생성)
)
* 자동리셋 이벤트
- 이벤트를 신호상태로 바꾸면 기다리는 스레드 중 하나만 깨운후 자동으로 비신호 상태가 된다.
따라서, 자동리셋 이벤트에 대해서는
ResetEvent()함수를 사용할 필요가 없다.
* 수동리셋 이벤트
- 이벤트를 신호상태로 바꾸면 계속 신호상태를 유지하므로 결과적으로 대기중인 스레드를 모두 깨우게 된다.
이 경우 비신호 상태로 바꾸려면 ResetEvent()함수를 사용하여야 한다.
* 이벤트 상태변경 함수
- BOOL SetEvent( 이벤트 핸들 )
// 비신호 → 신호- BOOL
ResetEvent( 이벤트 핸들)
// 신호 → 비신호
<이벤트 예제 코드>
- 하나의 스레드가 버퍼에 데이터를 쓰고, 나머지 스레드는 버퍼를 출력하는 예제
#include <windows.h>
#include <stdio.h>
#define BUFSIZE 16
HANDLE hReadEvent;
HANDLE hWriteEvent;
char 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] = 3;
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;
// 읽은 데이터를 출력
printf("Thread %d:\t", GetCurrentThreadId());
for(int i=0; i<BUFSIZE; i++)
printf("%d ", buf[i]);
printf("\n");
// 버퍼를 0으로 초기화
ZeroMemory(buf, sizeof(buf));
// WriteThread가 실행 안되면 출력값은 '0' 이다.
SetEvent(hReadEvent);
// 읽기 완료를 알림(Read이벤트 신호 상태로 변경)
}
return 0;
}
int main(void)
{
// 이벤트 생성 // Read와 Write 둘다 자동 리셋 이벤트로 생성 // 대기중인 스레드중 하나의 스레드만 이벤트 제어권을 넘겨받고 자동으로 비신호 상태가 된다.
hReadEvent = CreateEvent(NULL, FALSE, TRUE, NULL);
if(hReadEvent == NULL) return -1;
hWriteEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
if(hWriteEvent == NULL) return -1;
// 세 개의 스레드 생성
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);
printf("모든 작업을 완료했습니다.\n");
return 0;
}
=> Write이벤트와 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번스레드에 있는 코드가 한번 실행되고 무한 대기 상태에 빠짐
출처: http://adolys.tistory.com/entry/스레드-이벤트Thread-Event []
=================================
=================================
=================================
'프로그래밍 관련 > 언어들의 코딩들 C++ JAVA C# 등..' 카테고리의 다른 글
C/C++ 상속, 다중상속 관련 (0) | 2017.06.29 |
---|---|
C/C++ 스레드, IOCP 관련 (0) | 2017.06.28 |
[C++] string to int - string에서 int로 변환 (0) | 2017.06.14 |
std::string과 std::wstring간의 문자열 변환 관련 (0) | 2017.06.13 |
C++ 멤버변수 static const 관련 등등 (0) | 2017.03.30 |
댓글 영역