CreateThread는 생성된 쓰레드 내에서 CRL 계열의 함수를 사용할 때 TLS를 사용하지 않기 때문에 Thread-Safe하지 않은 문제가 있다는 것은 널리 알려져 있다. 대신 _beginthreadex 나 AfxBeginThread를 사용하라고들 얘길 하는데....

AfxBeginThread 함수의 문제는 아니지만... AfxBeginThread를 다른 쓰레드 생성 함수들처럼 사용해버리면 문제가 되곤 한다.

먼저 _beginthreadex로 쓰레드를 생성하는 샘플코드이다.


스레드 생성시에는 스레드 내부적으로 별도의 핸들을 가지고 있기 때문에 _beginthreadex가 리턴하는 핸들까지 하면 두개의 핸들이 생성된다. 따라서, 생성한 스레드를 부모 스레드에서 계속 관리할 것이 아니라면 바로 CloseHandle 해버리는 것이 자원을 관리하는 FM이다. (그러지 않는다면 나중에 스레드가 종료된 후에도 스레드 Object가 파괴되지 않을 것이다. 왜냐구? 핸들카운트가 0이 되지 않았기 때문이지!)

그런데... MFC계열의 AfxBeginThread는 사용법이 약간 다르다.
MSDN의 함수 Prototype은 다음과 같다.
CWinThread* AfxBeginThread(
                    AFX_THREADPROC
pfnThreadProc,
                         LPVOID pParam,
                         int nPriority = THREAD_PRIORITY_NORMAL,
                         UINT nStackSize = 0,
                         DWORD dwCreateFlags = 0,
                         LPSECURITY_ATTRIBUTES lpSecurityAttrs = NULL
                    );

일단... 리턴값이 다르다. 스레드의 HANDLE이 아니라 CWinThread 클래스의 포인터이다.
CWinThread? 이건 또 모야... MFC코드에는 다음과 같이 선언되어 있다.
class CWinThread : public CCmdTarget
{
    DECLARE_DYNAMIC(CWinThread)

public:
// Constructors
    CWinThread();
   // ... 중간생략

    // only valid while running
    HANDLE m_hThread;       // this thread's HANDLE
    operator HANDLE() const;
    DWORD m_nThreadID;      // this thread's ID

아항... CWinThread의 내부에 스레드 핸들을 가지고 있군.
흠. 그렇다면 이렇게 코드를 써주면 되겠구만


바로 이게 오바질이 되것다.
저렇게 하면... 스레드 종료할 때(정확히 말하면 CWinThread 개체 파괴시) 원인불명의 메모리 폴트가 뜨게 된다. 왜냐구?
AfxBeginThread에 의해 생성된 스레드가 종료될 때 CWinThread의 소멸자가 호출되는데, 이 소멸자 내에서 CWinThread 개체의 m_hHandle을 CloseHandle하도록 되어 있다. 이때 이미 Close된 핸들을 다시 Close하려고 시도하니까 문제가 발생하는 것이다.

AfxBeginThread로 스레드를 생성했을 경우에는 굳이 CloseHandle을 해줄 필요가 없다.
꼭 해주려면 다음과 같이 해주면 에러가 발생하지 않는다.




※ 만약 부모 스레드에서 자식스레드의 상태를 계속 관리해주어야 하는 경우에는
다음과 같이 스레드 종료 후 CWinThread가 자동 파괴되지 않도록 해주어야 한다.
Posted by kuaaan
,


사랑합니다. 편안히 잠드소서