_beginthreadex() 함수를 이용해 스레드를 성공적으로 생성하면 핸들을 반환한다. 그리고 생성한 스레드가 종료되면
CloseHandle 함수를 통해 핸들을 닫아야 한다는 걸 알고 있었다.
아래는 MSDN의 CloseHandle 글인데 해석해 보면 스레드 핸들을 닫는 행동이 관련된 스레드를 종료하거나 스레드 객체를 종료하진 않는다고 한다. 그리고 스레드 객체를 지우려면 스레드를 종료시키고 핸들을 닫으라고 한다.
Closing a thread handle does not terminate the associated thread or remove the thread object. Closing a process handle does not terminate the associated process or remove the process object. To remove a thread object, you must terminate the thread, then close all handles to the thread.
나는 여기서 스레드가 종료되면 CloseHandle()을 통해 닫아야 한다고 이해했다. 즉 스레드가 종료되기 전에 CloseHandle 함수를 호출하면 안되는 줄 알았다.
그런데 아래와 같이 스레드를 생성하고 wait 없이 별개로 계속해서 흐름을 진행하고 싶으면 어떻게 해야 하나 싶었다.
HANDLE tHandle = _beginthreadex(...);
// CloseHandle(tHandle);
왜냐하면 부모 스레드가 CloseHandle()을 호출할 때 자식 스레드가 아직 함수를 실행 중일 수 있으니까.
그래서 CloseHandle()에 대해 검색해 보니 다음과 같은 글을 찾았다. 질문은 스레드가 종료되기 전에 CloseHandle를 호출해도 문제없냐는 거였다.
답변은 오히려 해야 한다고 한다. 그래서 자료를 좀 더 찾아보니 핸들에 대한 참조 카운트를 알게 되었다.
스레드는 운영체제의 자원(resource)이고, 이러한 자원을 생성하면 운영체제에서 자원에 대한 커널 오브젝트를 생성한다. 커널 오브젝트에는 자원과 관련된 다양한 정보들이 저장되어 있다. 당연하게도 사용자의 프로그램에선 커널에 직접 접근이 불가능하다. 따라서 간접적으로 제어할 수 있도록 커널 오브젝트의 특정 자원에 대한 핸들(주소)를 넘겨주게 된다.
다른 자원은 잘 모르겠지만 우선 스레드에 대한 수명은 운영체제가 관리한다. 바로 참조 카운트 방식의 Usage Count라는 필드가 존재하며 이는 커널 오브젝트 내에서 관리된다. 이 방식은 자원을 참조하는 대상의 수를 계속 추적하고 있다가 아무도 참조하지 않으면, 즉 0이 되면 필요없는 자원이라 생각하고 지우는 방식이다.
우리가 스레드를 생성하면 Usage Count가 2가 된다. 1이 아닌 이유는? 생성된 스레드(+1)과 이 스레드를 생성해 준 부모 스레드에게도 역시 카운트가 +1 적용된다. 핸들을 반환했으니 부모 스레드에서도 접근이 가능하므로.
그리고 Usage Count가 0이 되면 운영체제가 이 스레드 자원을 지운다. 생성된 스레드가 종료되면 알아서 count가 1이 감소할 것이란건 쉽게 예상할 수 있다. 그럼 나머지 1은? 바로 CloseHandle 함수가 이 역할을 한다. CloseHandle() 함수를 호출하면 인자로 전달된 핸들의 count를 1 감소시킨다. 그럼 count가 0이 되어 운영체제가 이 스레드를 삭제한다. 때문에 스레드 생성 시 반환된 핸들은 꼭 가지고 있어야 한다.
결국 아래와 같은 코드가 있을 때 스레드가 종료되기 전에 CloseHandle 함수가 호출된다 해도 count는 2에서 1이 될 뿐이다. 그리고 스레드가 종료되면 나머지 1이 감소하여 0이 되고, 이후엔 운영체제가 알아서 삭제해 준다~
HANDLE tHandle = _beginthreadex(...);
CloseHandle(tHandle);
'게임 공부 > Windows API' 카테고리의 다른 글
[윈도우즈 API 정복] 1. 윈도우즈 프로그래밍 (0) | 2023.05.23 |
---|---|
[GDI+] Image::FromFile 결과가 NULL이 나올 때 (0) | 2020.10.29 |
A 스레드로 생성한 윈도우를 다른 스레드가 파괴할 수 없다! (0) | 2020.10.17 |
[WinAPI] Button 삭제하기 (0) | 2020.10.03 |
[WinAPI] GDI+를 이용하여 이미지 파일(png, jpg 등) 출력하기 (0) | 2020.09.19 |
댓글