본문 바로가기
프로그래밍/C

Win32 API ] waitForSingleObject, waitForMultipleObjects

by eteo 2023. 10. 14.

 

 

 

waitForSingleObject()

 

이 함수는 다양한 유형의 핸들에 대한 대기 및 감시를 지원한다. 핸들이 신호를 보낼 때까지 또는 타임아웃이 발생할 때까지 블록되며, 해당 이벤트가 발생하면 반환된다.

 

DWORD WaitForSingleObject(
  HANDLE hHandle,
  DWORD  dwMilliseconds
);
  • hHandle: 대기하려는 핸들(이벤트, 뮤텍스, 세마포어 등)
  • dwMilliseconds: 밀리초 단위 대기 제한 시간, 0을 사용하면 바로 반환하고 INFINITE를 사용하면 무한 대기한다.
  • DWORD 리턴값
    • WAIT_OBJECT_0(0): 대기가 성공적으로 종료된 경우. 대상 핸들이 스레드인 경우엔 스레드가 종료된경우, 대상 핸들이 뮤텍스인 경우엔 뮤텍스 락을 획득한 경우, 대상 핸들이 이벤트인 경우엔 이벤트가 시그널 상태로 전환된 경우를 의미한다.
    • WAIT_TIMEOUT(0x102): 대기 성공 종료 조건을 만족하지 않은 채 제한 시간이 초과된 경우.
    • WAIT_FAILED(0xFFFFFFFF): 함수 호출 자체가 실패한 경우

 

 

waitForSingleObject () 사용 예시.

 

#include <Windows.h>
#include <iostream>

int main() {
    HANDLE hEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
    if (hEvent == NULL) {
        std::cerr << "Failed to create event." << std::endl;
        return 1;
    }

    // 이벤트를 시그널 상태로 설정
    SetEvent(hEvent);

    // 이벤트 핸들이 시그널 상태가 될 때까지 대기
    DWORD result = WaitForSingleObject(hEvent, INFINITE);

    if (result == WAIT_OBJECT_0) {
        std::cout << "Event occurred." << std::endl;
    }
    else {
        std::cout << "Wait failed." << std::endl;
    }

    CloseHandle(hEvent);

    return 0;
}

 

 

 

 

 

 

waitForMultipleObjects()

 

DWORD WaitForMultipleObjects(
  DWORD        nCount,
  const HANDLE *lpHandles,
  BOOL         bWaitAll,
  DWORD        dwMilliseconds
);
  • nCount: 대기할 핸들 배열의 요소 수
  • lpHandles: 대기할 핸들 배열
  • bWaitAll: TRUE로 설정하면 모든 핸들이 시그널 상태가 될 때까지 대기하고 ,FALSE로 설정하면 하나 이상의 핸들이 시그널 상태가 되면 리턴한다.
  • dwMilliseconds: ms초 단위 대기 제한 시간. 0 또는 INFINITE 사용하면 바로 리턴하거나 무한 대기할 수 있다.
  • DWORD 리턴 값 : 대기중 이벤트가 발생하면 lpHandles 배열에서 해당 이벤트 핸들의 인덱스를 반환한다. 이를 통해 어떤 이벤트가 발생했는지 확인할 수 있다.

 

 

waitForMultipleObjects() 사용 예시.

 

#include <Windows.h>
#include <iostream>

int main() {
    HANDLE hEvent1 = CreateEvent(NULL, FALSE, FALSE, NULL);
    if (hEvent1 == NULL) {
        std::cerr << "Failed to create event." << std::endl;
        return 1;
    }
    HANDLE hEvent2 = CreateEvent(NULL, FALSE, FALSE, NULL);
    if (hEvent2 == NULL) {
        std::cerr << "Failed to create event." << std::endl;
        return 1;
    }

    HANDLE handles[2];
    handles[0] = hEvent1;
    handles[1] = hEvent2;

    // 첫 번째 이벤트를 시그널 상태로 설정
    SetEvent(hEvent2);

    // 두 이벤트 중 하나가 발생할 때까지 대기
    DWORD result = WaitForMultipleObjects(2, handles, FALSE, INFINITE);

    if (result == WAIT_OBJECT_0) {
        std::cout << "Event 1 occurred." << std::endl;
    }
    else if (result == WAIT_OBJECT_0 + 1) {
        std::cout << "Event 2 occurred." << std::endl;
    }
    else {
        std::cout << "Wait failed." << std::endl;
    }

    CloseHandle(hEvent1);
    CloseHandle(hEvent2);

    return 0;
}

 

 

 

지원하는 대기 및 감시의 종류 

 

 

1. 스레드 종료 대기 

 

CreateThread로 생성된 스레드의 핸들을 전달하여 해당 스레드가 종료될 때까지 대기한다. 

 

 

 

 

스레드 종료 대기 예시. 

 

1번 스레드에서 공유자원을 10000000만큼 증가시키고 2번 스레드에서는 1번 스레드 종료까지 대기했다가 공유자원을 2배로 증가시키는 예제

 

 

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

int sharedResource = 0; // 공유 자원
HANDLE hThread1, hThread2;

DWORD WINAPI ThreadFunction1(LPVOID lpParam) {
    // 1번 스레드: sharedResource를 증가
    for (int i = 0; i < 10000000; i++) {
        sharedResource++;
    }
    printf("Thread 1 finished. sharedResource = %d\n", sharedResource);

    return 0;
}

DWORD WINAPI ThreadFunction2(LPVOID lpParam) {
    // 2번 스레드: 1번 스레드가 종료될 때까지 대기
    WaitForSingleObject(hThread1, INFINITE);
    
    // 1번 스레드가 종료되면 sharedResource를 2배로 증가
    sharedResource *= 2;
    printf("Thread 2 finished. sharedResource = %d\n", sharedResource);

    return 0;
}

int main() {
    // 스레드 1 생성
    hThread1 = CreateThread(NULL, 0, ThreadFunction1, NULL, 0, NULL);
    if (hThread1 == NULL) {
        printf("CreateThread for Thread 1 failed, error %d\n", GetLastError());
        return 1;
    }

    // 스레드 2 생성
    hThread2 = CreateThread(NULL, 0, ThreadFunction2, NULL, 0, NULL);
    if (hThread2 == NULL) {
        printf("CreateThread for Thread 2 failed, error %d\n", GetLastError());
        return 1;
    }

    // 스레드 1 종료 대기
    WaitForSingleObject(hThread1, INFINITE);

    // 스레드 핸들 닫기
    CloseHandle(hThread1);
    CloseHandle(hThread2);

    return 0;
}

 

 

 

 

 

 

 

 

 

2. 뮤텍스 또는 세마포어 대기 

 

 

CreateMutex, CreateSemaphore와 같은 함수로 생성된 뮤텍스나 세마포어 핸들을 전달하여 다른 스레드나 프로세스가 해당 뮤텍스나 세마포어를 신호를 보낼 때까지 대기한다. 

 

 

 

뮤텍스 대기 예시. 

 

스레드를 4개만들고 WaitForSingleObject로 뮤텍스 락을 얻을 때까지 대기시켜 안전하게 공유자원에 액세스 한 뒤 뮤텍스 락을 해제해 다른 스레드가 접근할 수 있도록 하는 예제

 

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

#define NUM_THREADS 4  // 테스트할 스레드 개수

// 공유 자원
int sharedResource = 0;

// 뮤텍스 핸들
HANDLE hMutex;

DWORD WINAPI ThreadFunction(LPVOID lpParam) {
    for (int i = 0; i < 10000; i++) {
        // 뮤텍스 락을 얻기 위해 대기
        DWORD dwWaitResult = WaitForSingleObject(hMutex, INFINITE);

        if (dwWaitResult == WAIT_OBJECT_0) {
            // 뮤텍스 락 획득 성공
            sharedResource++; // 공유 자원을 수정

            // 뮤텍스 락 해제
            ReleaseMutex(hMutex);
        } else {
            // 뮤텍스 락 실패
            printf("WaitForSingleObject failed, error %d\n", GetLastError());
        }
    }

    return 0;
}

int main() {
    // 뮤텍스 생성
    hMutex = CreateMutex(NULL, FALSE, NULL);
    if (hMutex == NULL) {
        printf("CreateMutex failed, error %d\n", GetLastError());
        return 1;
    }

    // 여러 개의 스레드 생성 및 실행
    HANDLE hThreads[NUM_THREADS];
    DWORD dwThreadIds[NUM_THREADS];

    for (int i = 0; i < NUM_THREADS; i++) {
        hThreads[i] = CreateThread(NULL, 0, ThreadFunction, NULL, 0, &dwThreadIds[i]);
        if (hThreads[i] == NULL) {
            printf("CreateThread failed, error %d\n", GetLastError());
            return 1;
        }
    }

    // 스레드 종료 대기
    WaitForMultipleObjects(NUM_THREADS, hThreads, TRUE, INFINITE);

    // 뮤텍스 핸들 닫기
    CloseHandle(hMutex);

    // 스레드 핸들 닫기
    for (int i = 0; i < NUM_THREADS; i++) {
        CloseHandle(hThreads[i]);
    }

    printf("Final sharedResource value: %d\n", sharedResource);

    return 0;
}

 

 

 

 

 

3. 이벤트 핸들 대기 

 

CreateEvent 함수로 생성된 이벤트 핸들을 전달하여 이벤트가 시그널될 때까지 대기할 수 있다.