Použití objektů Mutex

Objekt mutex můžete použít k ochraně sdíleného prostředku před souběžným přístupem několika vlákny nebo procesy. Každé vlákno musí před spuštěním kódu, který přistupuje ke sdílenému prostředku, nejprve získat mutex. Pokud například několik vláken sdílí přístup k databázi, mohou vlákna použít objekt mutex k povolení zápisu do databáze pouze jedním vláknem najednou.

Následující příklad používá funkci CreateMutex k vytvoření objektu mutex a CreateThread funkce k vytvoření pracovních vláken.

Když vlákno tohoto procesu zapisuje do databáze, nejprve si vyžádá vlastnictví objektu mutex pomocí funkce WaitForSingleObject. Pokud vlákno získá vlastnictví mutexu, zapíše do databáze a poté uvolní vlastnictví mutexu pomocí funkce ReleaseMutex.

Warning

Riziko zablokování při použití časového limitu INFINITE: Níže uvedený příklad používá INFINITE jako parametr časového limitu pro funkci WaitForSingleObject. V produkčním kódu to znamená, že volající vlákno zůstane zablokované na neurčito, pokud vlákno, které mutex vlastní, uvízne v zablokování, přestane reagovat nebo mutex neuvolní. Zvažte použití konečné hodnoty časového limitu a implementaci logiky opakování s diagnostickým protokolováním k detekci potenciálních zablokování. K diagnostice uváznutí mutexu použijte nástroje, jako jsou rozšíření ladicího programu !handle (např. !handle <handle> f) nebo procházení řetězce čekání (GetThreadWaitChain).

Tento příklad používá strukturované zpracování výjimek k zajištění toho, aby vlákno správně uvolní objekt mutex. Blok __finally kódu se spustí bez ohledu na to, jak se blok __try ukončí (pokud blok __try neobsahuje volání funkce TerminateThread ). Tím zabráníte neúmyslně opuštění objektu mutex.

Pokud je mutex opuštěn, vlákno, které mutex vlastnilo, jej před ukončením řádně neuvolnilo. V tomto případě je stav sdíleného prostředku neurčitý a nadále používat mutex může zakrýt potenciálně závažnou chybu. Některé aplikace se mohou pokusit uvést prostředek do konzistentního stavu; tento příklad jednoduše vrátí chybu a mutex již dále nepoužívá. Další informace naleznete v tématu Mutex Objects.

#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; 
}