산타는 없다

Window via C/C++ 9장 - 커널 오브젝트를 이용한 스레드 동기화 본문

프로그래밍 서적/Window via C++

Window via C/C++ 9장 - 커널 오브젝트를 이용한 스레드 동기화

LEDPEAR 2021. 11. 7. 18:03
반응형
  • 0. 개요
    • 유저 모드 동기화의 최대 장점은 빠르다는 것이다. 스레드의 수행 성능이 중요한 경우라면 항상 유저 모드 스레드 동기화 메커니즘을 가장 먼저 고려해 보아야 한다.
    • 유저 모드에서 커널 모드로의 전환은 약 200CPU 사이클 정도가 필요한 비싼 작업이다.
    • 프로세스 커널 오브젝트의 경우 관련된 프로세스가 종료되면 운영체제가 자동적으로 해당 오브젝트를 시그널 상태로 변경한다. 프로세스 커널 오브젝트의 경우 한 번 시그널 상태가 되면 다시 논시그널 상태로 변경될 수 없으며 영원히 시그널 상태로 남게 된다.
    • 프로세스 커널 오브젝트의 내부에는 오브젝트 생성 시 FALSE(논시그널)로 초기화되는 BOOL 값이 있는데, 이 값은 프로세스가 종료되면 운영체제에 의해 자동적으로 TURE로 변경되어 해당 커널 오브젝트가 시그널 상태임을 나타내게 된다.
  • 1. 대기 함수들
    • 대기 함수를 호출하면 인자로 전달한 커널 오브젝트가 시그널 상태가 될 때까지 이 함수를 호출한 스레드를 대기 상태로 유지한다. 만일 대기 함수가 호출된 시점에 커널 오브젝트가 이미 시그널 상태였다면 스레드는 대기 상태로 전환되지 않는다.
    • 대기 함수 중 가장 많이 쓰이는 함수는
      - DWORD WaitForSingleObject(HANDLE hObject, DWORD dwMilliseconds);
      • hObject : 시그널과 논시그널 상태가 될 수 있는 커널 오브젝트의 핸들
      • dwMilliseconds : 커널 오브젝트가 시그널 상태가 될 때까지 얼마나 오랫동안 기다려 볼 것인지를 나타내는 시간 값
    • DWORD WaitForMultipleObjects(DWORD dwCoutn, CONST HANDLE* phObjects, BOOL bWaitAll,                                               DWORD dwMilliseconds);
      • 하나가 아닌 여러 개의 커널 오브젝트들에 대해 시그널 상태를 동시에 검사할 수 있다.
      • dwCount : 검사해야 하는 커널 오브젝트의 개수
      • phObjects : 커널 오브젝트 핸들의 배열을 가리키는 포인터
      • bWaitAll : 모든 오브젝트가 시그널 상태가 될 때가 대기 할 것인지(TRUE), 하나라도 시그널 상태가 되면 빠져나올 것인지(FALSE)
      • dwMilliseconds : 얼마나 오래 기다릴 것인지 나타내는 시간값
  • 2. 성공적인 대기의 부가적인 영향
    • 성공적인 호출 : 매개변수로 전달한 커널 오브젝트가 시그널 상태가 되어 WAIT_OBJECT_0을 반환하는 경우
    • 성공적이지 않은 호출 : WAIT_TIMEOUT이나 WAIT_FAILED를 반환하는 경우를 말하며 오브젝트의 상태가 변경되지 않는다.
    • ① 자동 리셋 이벤트 커널 오브젝트 핸들을 매개변수로 대기 함수를 호출하는 경우, 이 오브젝트가 시그널 상태가 되면 WAIT_OBJECT_0을 반환한다
    • ② 함수가 반환되기 직전에 이벤트 커널 오브젝트가 논시그널 상태로 변경된다
    • 마이크로소프트가 사용하고 있는 스레드 순서 알고리즘은 '선입선출' 방식이지만 이러한 동작은 예고 없이 변경될 수 있다.
  • 3. 이벤트 커널 오브젝트
    • 모든 커널 오브젝트 중 이벤트가 가장 단순한 구조를 가지고 있다. 이벤트는 사용 카운트(모든 커널 오브젝트가 가지고 있는), 자동 리셋 이벤트인지 수동 리셋 이벤트인지 판별하는 BOOL 값, 이벤트가 시그널 상태인지 논시그널 상태인지를 나타내는 BOOL값으로 이루어져 있다
    • 이벤트는 어떤 작업이 완료되었음을 알리기 위해 주로 사용되며, 수동 리셋 이벤트가 시그널 상태가 되면 이 이벤트를 기다리고 있던 모든 스레드들은 동시에 스케줄 가능 상태가 된다. 자동 리셋 이벤트의 경우에는 대기 중인 스레드들 중 하나의 스레드만이 스케줄 가능 상태가 된다.
    • 이벤트는 하나의 스레드가 초기 작업을수행하고 이후 다른 스레드에게 나머지 작업을 수행할 것을 알려주기 위해 사용하는 경우가 많다.
    • 마이크로소프트는 두 가지 형태의 이벤트 중 자동 리셋 이벤트에 대해서만 성공적인 대기의 부가적인 영향을 정의하고 있다. 만일 자동 리셋 이벤트에 대해 성공적인 대기가 이루어지면 자동적으로 이벤트의 상태는 논시그널로 바뀐다.
    • BOOL PulseEvent(HANDLE hEvent) : 이벤트를 시그널 상태로 변경하였다가 곧바로 다시 논시그널 상태로 변경한다. 수동 리셋 이벤트를 인자로 호출하면 이 이벤트가 시그널 상태가 되기를 기다리던 모든 스레드가 한꺼번에 스케줄 가능 상태가 되며, 자동 리셋 이벤트를 인자로 호출하면 그 중 하나의 스레드만이 스케줄 가능 상태가 된다.
    • ① 핸드셰이크 예제 애플리케이션
  • 4. 대기 타이머 커널 오브젝트
    • 대기 타이머(waitable timer) : 특정 시간에 혹은 일정한 간격을 두고 자신을 시그널 상태로 만드는 커널 오브젝트로서, 주로 특정 시간에 맞추어 어떤 작업을 수행해야 할 경우에 사용
      HANDLE CreateWaitableTimer(PSECURITY_ATTRIBUTES psa, BOOL bManualReset, PCTSTR pszName);
    • 다른 프로세스에서는 OpenWaitableTimer 함수를 이용하여 이미 생성된 대기 타이머를 가리키는 프로세스 고유의 핸들 값을 얻을 수 있다.
      HANDLE OpneWaitableTimer(DWORD dwDesiredAccess, BOOL bManualReset, PCTSTR pszName);
    • bManualReset : 수동 리셋 타이머를 생성할 것인지 아니면 자동 리셋 타이머인지를 생성할 것인지를 결정하는 값을 전달한다. 자동 라셋 타이머가 시그널 상태가 되면 이 타이머를 대기 중인 스레드들 중 유일하게 한 개의 스레드만이 스케줄 가능 상태가 된다.
    • 대기 타이머는 항상 논시그널 상태로 생성되며, 언제 시그널 상태가 될것인지 지정하기 위해 SetWaitableTimer 함수를 사용한다
      BOOL SetWaitableTimer(HANDLE hTimer, const LARGE_INTEGER *pDueTime, LONG lPeriod,                                            PTIMERAPCROUTINE pfnCompletionRoutine,                                                                        PVOID pvArgToCompletionRoutine, BOOL bResume);
    • ① 대기 타이머를 이용하여 APC 요청을 스레드의 APC 큐에 삽입하는 방법
      • 마이크로소프트는 SetWaitableTimer를 이용하여 타이머가 시그널 상태가 되었을 때 비동기 함수 호출(asynchronous procedure call / APC) 요청을 스레드의 APC 큐에 삽입할 수 있는 방법을 제공하고 있다.
      • 스레드는 단일의 타이머 핸들에 대해 타이머 커널 오브젝트에 대한 시그널 대기와 얼러터블 상태 대기를 동시에 수행해서는 안된다.
    • ② 타이머와 관련된 미결 문제
      • 타이머는 종종 통신 프로토콜을 구현하는데 사용되기도 한다. 이때, 매 요청별로 타이머 커널 오브젝트를 생성하게 되면 시스템의 성능이 저하될 수도 있기 때문에 가능하다면 하나의 타이머 오브젝트만을 생성하고, 시그널 시간을 적절히 변경해 가면서 재사용하는 것이 좋다.
      • 직접 타이머 시간을 변경하고 재설정하는 작업을 수행하는 대신 새롭게 추가된 스테드 풀링 함수의 하나인 CreateThreadpoolTimer와 같은 함수를 사용한다.
      • 윈도우 개발에 익숙한 개발자라면 대기 타이머와 유저타이머(SetTimer 함수를 사용하는)를 비교해 보려 할 것이다. 가장 큰 차이점은 유저 타이머의 경우 비교적 리소스를 많이 사용하는 사용자 인터페이스 환경 하에서만 수행된다는 것이다. 대기 타이머는 커널 오브젝트이기 때문에 다수의 스레드에 의해 공유될 수 있으며, 좀더 보안에 안정적이다.
  • 5. 세마포어 커널 오브젝트
    • 세마포어 커널 오브젝트는 리소스의 개수를 고려해야 하는 상황에서 주로 사용된다. 이 커널 오브젝트는 모든 커널 오브젝트와 마찬가지로 사용 카운트를 가지고 있으며, 이 외에도 2개의 32비트 값을 가지고 있어서 최대 리소스 카운트와 현재 리소스 카운트를 저장하고 있다. 최대 리소스 카운트는 세마포어가 제어할 수 있는 리소스의 최대 개수를 나타내는 데 사용되고, 현재 리소스 카운트는 사용 가능한 리소스의 개수를 나타내는 데 사용된다.
    • 세마포어는 다음의 규칙에 따라 동작한다.
      • 현재 리소스 카운트가 0보다 크면 세마포어는 시그널 상태가 된다.
      • 현재 리소스 카운트가 0이면 세마포어는 논시그널 상태가 도니다.
      • 시스템은 현재 리소스 카운트를 음수로 만들 수 없다.
      • 현재 리소스 카운트는 최대 리소스 카운트보다 커질 수 없다.
    • 세마포어를 사용할 때에는 오브젝트의 사용 카운트와 현재 리소스 카운트를 혼돈하지 않도록 주의해야 한다.
    • 세마포어 커널 오브젝트를 생성하려면 CreateSemaphore 함수를 사용하면 된다.
      - HANDLE CreateSemaphore(PSECURITY_ATTRIBUTE psa, LONG lInitialCount,                                                                      LONG lMaximumCount, PCTSTR pszName);
    • CreateSemaphoreEx 함수를 이용하면 dwDesiredAccess 매개변수를 통해 세마포어에 대한 접근 권한을 바로 지정할 수 있다. dwFlags 매개변수는 항상 0으로 설정해야 한다..
      - HANDLE CreateSemaphoreEx(PSECURITY_ATTRIBUTE psa, LONG lInitialCount,                                                                     LONG lMaximumCount, PCTSTR pszName, DWORD dwFlags,                                                   DWORD dwDesiredAccess);
    • 모든 프로세스는 OpenSemaphore 함수를 이용하여 이미 생성된 세마포어를 가리키는 프로세스 고유의 핸들 값을 얻을 수 있다.
      - HANDLE OpenSemaphore(DWORD dwDesiredAccess, BOOL bInheritHandle, PCTSTR pszName);
    • 세마포어의 현재 리소스 카운트를 증가시키기 위해서는 ReleaseSemaphore 함수를 호출하면 된다.
      - BOOL ReleaseSemaphore(HANDLE hSemaphore, LONG lReleaseCount, PLONG plPreviousCount);
    • 세마포어의 현재 리소스 카운트 값을 변경하지 않도로 그 값을 알수 있는 방법은 없다.
  • 6. 뮤텍스 커널 오브젝트
    • 뮤텍스 커널 오브젝트는 스레드가 단일의 리소스에 대해 배타적으로 접근할 수 있도록 해 준다. 사실 뮤텍스(MUTual EXclusion)이라는 이름도 이러한 특성으로부터 기인한 것이다. 이 커널 오브젝트는 사용 카운트, 스레드ID, 반복 카운터를 저장할 수 있는 공간을 가지고 있다.
    • 뮤텍스의 동작 방식은 크리티컬 섹션과 동일하다. 하지만 크리티컬 섹션이 유저 모드 동기화 오브젝트인 데 반해 뮤텍스는 커널 오브젝트라는 차이점이 있다. 이러한 차이점 때문에 뮤텍스는 크리티컬 섹션에 비해 느리지만, 서로 다른 프로세스에서 동일 뮤텍스에 대해 접근이 가능하며, 리소스에 대한 접근 권한을 획득할 때 시간 제한을 지정할 수 있다는 장점이 있다.
    • 스레드 ID : 시스템 내의 어떤 스레드가 뮤텍스를 소유하고 있는지를 나타내는 값이다.
    • 반복 카운터 : 뮤텍스를 소유하고 있는 스레드가 몇 회나 반복적으로 뮤텍스를 소유하고자 했는지에 대한 횟수를 나타내는 값이다.
    • 뮤텍스는 다수의 스레드가 동시에 접근하는 메모리 블록을 보호하기 위해 사용되기도 한다.
    • 뮤텍스는 다음의 규칙에 따라 동작한다.
      • 스레드ID가 0(유효하지 않는 스레드ID)이면 뮤텍스는 어떠한 스레드에 의해서도 소유되지 않은 것이며, 이때 뮤텍스는 시그널 상태가 된다.
      • 스레드ID가 0이 아니면 뮤텍스는 특정 스레드에 의해 소유된 것이며, 이때 논시그널 상태가 된다.
      • 다른 커널 오브젝트와는 다르게 뮤텍스는 특수한 코드를 포함하고 있어서 일반적인 규칙을 위반하는 경우도 있다.
    • 뮤텍스를 사용하려면 CreateMutex 함수를 호출해서 뮤텍스를 생성해야 한다.
      - HANDLE CreateMutex(PSECURITY_ATTRIBUTES psa, BOOL bInitialOwner, PCTSTR pszName);
    • CreateMutexEx 함수를 이용하면 dwDesiredAccess 매개변수를 통해 뮤텍스에 대한 접근 권한을 바로 지정할 수 있다. dwFlags 매개변수는 CreateMutex의 bInitialOwner 매개변수와 동일한 용도로 사용된다: 0은 FALSE를 CREATE_MUTEX_INITIAL_OWNEL는 TRUE와 동일한 의미로 사용된다.
      - HANDLE CreateMutexEx(PSECURITY_ATTRIBUTES psa, PCTSTR pszName, DWORD dwFlags,                                              DWORD dwDesiredAccess);
    • 모든 프로세스는 OpenMutex 함수를 이용하여 이미 생성된 뮤텍스를 가리키는 프로세스 고유의 핸들값을 얻을 수 있다.
      - HANDLE OpenMutex(DWORD dwDesiredAccess, BOOL bInheritHandle, PCTSTR pszName);
    • bInitialOwner 매개변수는 뮤텍스의 초기 상태를 제어하는 용도로 사용된다. 이값을 FALSE(보통의 경우)로 설정하면 뮤텍스의 스레드ID와 반복 카운터는 0으로 설정된다. 이것은 뮤텍스가 어떠한 스레드에 의해서도 소유되지 않았으며, 시그널 상태임을 나타내게 된다. 만일 bInitialOwner 값을 TURE로 설정하게 되면 뮤텍스의 스레드ID는 함수를 호출한스레드의 ID로 설정되며, 반복 카운터는 1로 설정된다. 스레드ID가 0이 아니므로 뮤텍스는 논시그널 상태가 된다.
    • 리소스에 대한 접근 권한을 획득한 스레드가 더 이상 리소스를 사용할 필요가 없어지면 반드시 ReleaseMutex 함수를 호출하여 뮤텍스의 소유권을 해제해 주어야 한다
      - BOOL ReleaseMutex(HANDLE hMutex);
    • ① 버림 문제 (Abandonment issues)
      • 뮤텍스는 다른 모든 커널 오브젝트와는 다르게 "스레드 소유권(thread ownership)"의 개념을 가지고 있다. 뮤텍스만이 어떤 스레드가 성공적인 대기를 수행하였는지를 기록해 둔다. 이러한 뮤텍스의 스레드 소유권이라는 개념 때문에 뮤텍스가 논 시그널 상태임에도 불구하고 스레드가 뮤텍스를 다시 소유할 수 있는 예외적인 규칙을 가지게 된 것이다.
      • 뮤텍스와 스레드 커널 오브젝트를 계속해서 추적하고 있기 때문에 언제 뮤텍스가 벼려졌는지(소유권을 해제하지 않고 스레드 종료) 정확히 알 수 있으며, 뮤텍스의 버림이 발생하면 버려진 뮤텍스의 스레드 ID와 반복 카운트를 0으로 변경한다. 이 경우 대기 함수는 WAIT_ABANDONED라는 특별한 값을 반환한다.
    • ② 뮤텍스와 크리티컬 섹션
      •  
        특성 뮤텍스 크리티컬 섹션
        성능 느림 빠름
        프로세스들 간에
        사용 가능 여부
        가능 불가능
        선언 HANDLE hmtx; CRITICAL_SECTION cs;
        초기화 hmtx
        = CreateMutex(NULL, FALSE, NULL);
        InitializeCriticalSection(&cs);
        삭제 CloseHandle(hmtx); DeleteCriticalSection(&cs);
        무한 대기 WaitForSingleObject(hmtx, INFINITE); EnterCriticalSection(&cs);
        0 대기 WaitForSingleObject(hmtx, 0); TryEnterCriticalSection(&cs);
        임의 시간 대기 WaitForSingleObject(hmtx, dwMilliseconds); 불가능
        해제 ReleaseMutex(hmtx); LeaveCriticalSection(&cs);
        다른 커널 오브젝트와 함께
        개기 가능 여부
        가능 (WaitForMultipleObjects나
        유사 함수를 이용)
        불가능
    • ③ 큐 예제 애플리케이션
  • 7. 편리한 스레드 동기화 오브젝트 표
    • 오브젝트 논시그널 상태 시그널 상태 성공적인 대기의
      부가적인 영향
      프로세스 프로세스가 수행 중 프로세스가 종료됨
      (ExitProcess,
      TerminateProcess)
      없음
      스레드 스레드가 수행 중 스레드가 종료됨
      (ExitThread,
      TerminateThread)
      없음
      잡 타임을 초과하지 않음 잡 타임 초과 없음
      파일 I/O 요청이 수행 중 I/O 요청이 완료됨 없음
      콘솔 입력 입력이 없음 입력이 있음 없음
      파일 변경 통지 파일이 변경되지 않음 파일시스템이 파일의 변경사항이 있음을 확인 통지를 리셋
      자동 리셋 이벤트 ResetEvent, PulseEvent, 또는 성공적인 대기 SetEvent/PulseEvent가 호출됨 이벤트 리셋
      수동 리셋 이벤트 ResetEvent, PulseEvent SetEvent/PulseEvent가 호출됨 없음
      자동 리셋 대기 타이머 CancelWaitableTimer
      또는 성공적인 대기
      설정한 시간에 도달함
      (setWaitableTimer)
      타이머 리셋
      수동 리셋 대기 타이머 CancelWaitableTimer 설정한 시간에 도달함
      (setWaitableTimer)
      없음
      세마포어 성공적인 대기 카운트가 0보다 큼
      (ReleaseSemaphore)
      카운트 1 감소
      뮤텍스 성공적인 대기 스레드에 의해 소유되지 않음(ReleaseMutex) 스레드가 소유권을 가짐
      크리티컬 섹션
      (유저모드)
      성공적인 대기
      ((Try)EnterCriticalSection)
      스레드에 의해 소유되지 않음
      (LeaveCriticalSection)
      스레드가 소유권을 가짐
      SRWLock
      (유저모드)
      성공적인 대기
      (AcquireSRWLock
      (Exclusive))
      스레드에 의해 소유되지 않음
      (ReleaseSRWLock 
      (Exclustive))
      스레드가 소유권을 가짐
      조건변수(유저모드) 성공적인 대기
      (SleepConditionVariable)
      깨어남 (Wake (All)
      condition Variable)
      없음
  • 8. 그 외의 스레드 동기화 함수들
    • ① 비동기 장치 I/O
      • 비동기 장치 I/O란 스레드가 읽기/쓰기 동작을 수행할 때 요청한 동작을 완료할 때까지 대기하지 않고 다른 작업을 수행할 수 있게 해 주는 방식이다.
      • 예를 들어 아주 큰 파일을 메모리로 읽어 와야 하는 경우 시스템에게 해당 파일을 메모리로 읽어 올 것을 명령하고 시스템이 파일을 읽는 동안 다른 작업을 수행할 수 있도록 해 주는 방식
    • ② WaitForInputIdle
      • 스레드는 WaitForInputIdle 함수를 호출하여 대기 상태로 진입할 수 있다.
        - DWORD WaitForInputIdle(HANDLE hProcess, DWORD dwMilliseconds)
      • 이 함수를 호출하면 hProcess가 가리키는 프로세스의 첫 번째 윈도우를 생성한 스레드가 대기 상태가 될 때까지 WaitForInputIdle 함수를 호출한 스레드를 대기 상태로 유지한다.
      • 이 함수는 페어런트 프로세스가 CreateProcess를 호출하여 차일드 프로세스의 생성을 요청한 후 차일드 프로세스가 완전히 초기화될 때까지 대기하도록 하고 싶은 경우에 유용하게 사용될 수 있다.
    • ③ MsgWaitForMulipleObjects(Ex)
      • MsgWaitForMulipleObjects나 MsgWaitForMulipleObjectsEx 함수를 사용하여 스레드가 메시지를 대기하도록 할 수 있다
      • 이 함수는 WaitForMulipleObjects 함수와 매우 유사하다. 차이점이라면 이 함수들은 커널 오브젝트가 시그널될 때 외에도 이 함수를 호출한 스레드가 생성한 윈도우에 메시지가 전달되었을 경우에도 스케줄 가능 상태가 된다는 것이다.
      • 윈도우를 생성하고 사용자 인터페이스와 관련된 작업을 수행하는 스레드라면 사용자 인터페이스가 응답하지 않는 상황을 피해 WaitForMulipleObjects 대신 이 함수를 사용하는 것이 좋다.
    • ④ WaitForDebugEvent
      • 디버거(debugger)가 수행되고 디버기(debugee)가 연결되면 디버거는 운영체제가 디버기와 관련된 디버그 이벤트를 전달해 줄 때까지 유휴 상태로 대기하게 된다. 디버거가 디버그 이벤트를 기다리기 위해서는 이 함수를 호출하면 된다.
      • 디버거 이 함수를 호출하면 디버거의 스레드는 대기 상태가 된다. 시스템은 디버그 이벤트가 발생한 경우 WaitForDebugEvent가 반환되도록 하여 디버그 이벤트가 발생하였음을 알려준다. pde 매개변수가 가리키는 구조체는 디버거의 스레드가 깨어나기 전에 시스템에 의해 채워지며, 어떤 디버그 이벤트가 발생했는지에 대한 정보를 포함하고 있다.
    • ⑤ SignalObjectAndWait
      • 특정 커널 오브젝트를 시그널 상태로 만들어주고, 이와는 또 따른 커널 오브젝트가 시그널 상태가 되기를 대기하는 기능을 원자적으로 수행한다.
      • DWORD SignalObjectAndWait(
                HANDLE hObjectToSignal,
                HANDLE hObjectToWaitOn,
                DWORD dwMilliseconds,
                BOOL bAlertable);
      • 이 함수를 호출할 때에는 hObjectToSignal 매개변수로 뮤텍스, 세마포어, 또는 이벤트가 전달되어야 하며, 다른 형태의 오브젝트 핸들이 전달되면 WAIT_FAILED가 반환되며, 이때 GetLastError를 호출하면 EEROR_INVALID_HANDLE이 반환된다. 이 함수는 전달되는 핸들의 오브젝트 타입에 맞추어 내부적으로 ReleaseMutex, ReleaseSemaphore(1을 인자로), 또는 SetEvent 함수를 각각 호출해 준다.
      • hObjectToWaitOn 매개변수로는 뮤텍스, 세마포어, 이벤트, 타이머, 프로세스, 스레드, 잡, 콘솔 입력, 그리고 변경 통지에 대한 핸들을 전달할 수 있다.
      • dwMilliseconds : 얼마만큼 대기할 것인지 지정
      • bAlertable : 스레드가 대기 상태인 동안 비동기 프로세저 호출을 수행할 수 있도록 할지의 여부
      • 이 함수는 두 가지 이유로 인해 윈도우에 추가되었다.
        • 첫째, 개발자들은 특정 오브젝트를 시그널 상태로 만들어준 후 다른 오브젝트를 대기하는 식의 코드를 자주 작성하게 되는데, 단일 함수로 이와 같은 작업을 수행하게 되면 수행 시간을 절약해 주는 효과가 있다.
        • 둘째, SignalObjectAndWait 함수를 이용하면 이 함수를 호출한 스레드가 대기 상태에 있음을 보증할 수 있기 때문에 앞서 설명한 PulseEvent와 같은 함수를 사용할 때 유용하게 활용될 수 있다. PulseEvent는 특정 이벤트 시그널 상태로 변경하였다가 그 즉시 논시그널 상태로 변경하게 되는데, 어떠한 이벤트를 대기 중인 스레드가 없다면 이벤트가 시그널되었는지를 감지할 수가 없다.
    • ⑥ 대기 목록 순회 API를 이용한 데드락 감지
반응형
Comments