뮤텍스 개체 사용

뮤텍스 개체 사용하여 여러 스레드 또는 프로세스의 동시 액세스로부터 공유 리소스를 보호할 수 있습니다. 각 스레드는 공유 리소스에 액세스하는 코드를 실행하기 전에 뮤텍스의 소유권을 기다려야 합니다. 예를 들어 여러 스레드가 데이터베이스에 대한 액세스를 공유하는 경우 스레드는 뮤텍스 개체를 사용하여 한 번에 하나의 스레드만 데이터베이스에 쓸 수 있도록 허용할 수 있습니다.

다음 예제에서는 CreateMutex 함수를 사용하여 뮤텍스 개체를 만들고 CreateThread 함수를 사용하여 작업자 스레드를 만듭니다.

이 프로세스의 스레드가 데이터베이스에 쓰면 먼저 WaitForSingleObject 함수를 사용하여 뮤텍스의 소유권을 요청합니다. 스레드가 뮤텍스의 소유권을 얻는 경우 데이터베이스에 쓴 다음 ReleaseMutex 함수를 사용하여 뮤텍스의 소유권을 해제합니다.

Warning

INFINITE 시간 제한이 있는 교착 상태 위험: 아래 예제에서는 INFINITE에 대한 시간 제한 매개 변수로 사용합니다. 프로덕션 코드에서 이는 소유 스레드가 교착 상태에 빠지거나 중단되거나 뮤텍스를 해제하지 못하는 경우 호출 스레드가 무기한 차단됩니다. 유한 시간 제한 값을 사용하고 진단 로깅을 사용하여 재시도 논리를 구현하여 잠재적 교착 상태를 검색하는 것이 좋습니다. !handle 디버거 확장(예: !handle <handle> f) 또는 Wait Chain Traversal(GetThreadWaitChain)과 같은 도구를 사용하여 뮤텍스 교착 상태를 진단합니다.

이 예제에서는 구조적 예외 처리를 사용하여 스레드가 뮤텍스 개체를 제대로 해제하는지 확인합니다. 코드의 __finally 블록은 __try 블록이 종료되는 방식에 관계없이 실행됩니다(__try 블록에 TerminateThread 함수에 대한 호출이 포함되어 있지 않은 경우). 이렇게 하면 뮤텍스 개체가 실수로 버려지지 않습니다.

뮤텍스가 중단된 경우 뮤텍스를 소유한 스레드가 종료하기 전에 뮤텍스를 제대로 해제하지 못했습니다. 이 경우 공유 리소스의 상태가 확정되지 않으며 뮤텍스를 계속 사용하면 잠재적으로 심각한 오류가 발생할 수 있습니다. 일부 애플리케이션은 리소스를 일관된 상태로 복원하려고 시도할 수 있습니다. 이 예제에서는 단순히 오류를 반환하고 뮤텍스 사용을 중지합니다. 자세한 내용은 뮤텍스 개체참조하세요.

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

#define THREADCOUNT 2

HANDLE ghMutex; 

DWORD WINAPI WriteToDatabase( LPVOID );

int main( void )
{
    HANDLE aThread[THREADCOUNT];
    DWORD ThreadID;
    int i;

    // Create a mutex with no initial owner

    ghMutex = CreateMutex( 
        NULL,              // default security attributes
        FALSE,             // initially not owned
        NULL);             // unnamed mutex

    if (ghMutex == NULL) 
    {
        printf("CreateMutex error: %d\n", GetLastError());
        return 1;
    }

    // Create worker threads

    for( i=0; i < THREADCOUNT; i++ )
    {
        aThread[i] = CreateThread( 
                     NULL,       // default security attributes
                     0,          // default stack size
                     (LPTHREAD_START_ROUTINE) WriteToDatabase, 
                     NULL,       // no thread function arguments
                     0,          // default creation flags
                     &ThreadID); // receive thread identifier

        if( aThread[i] == NULL )
        {
            printf("CreateThread error: %d\n", GetLastError());
            return 1;
        }
    }

    // Wait for all threads to terminate

    WaitForMultipleObjects(THREADCOUNT, aThread, TRUE, INFINITE);

    // Close thread and mutex handles

    for( i=0; i < THREADCOUNT; i++ )
        CloseHandle(aThread[i]);

    CloseHandle(ghMutex);

    return 0;
}

DWORD WINAPI WriteToDatabase( LPVOID lpParam )
{ 
    // lpParam not used in this example
    UNREFERENCED_PARAMETER(lpParam);

    DWORD dwCount=0, dwWaitResult; 

    // Request ownership of mutex.

    while( dwCount < 20 )
    { 
        dwWaitResult = WaitForSingleObject( 
            ghMutex,    // handle to mutex
            INFINITE);  // no time-out interval
 
        switch (dwWaitResult) 
        {
            // The thread got ownership of the mutex
            case WAIT_OBJECT_0: 
                __try { 
                    // TODO: Write to the database
                    printf("Thread %d writing to database...\n", 
                            GetCurrentThreadId());
                    dwCount++;
                } 

                __finally { 
                    // Release ownership of the mutex object
                    if (! ReleaseMutex(ghMutex)) 
                    { 
                        // Handle error.
                    } 
                } 
                break; 

            // The thread got ownership of an abandoned mutex
            // The database is in an indeterminate state
            case WAIT_ABANDONED: 
                return FALSE; 
        }
    }
    return TRUE; 
}