Använda Mutex-objekt

Du kan använda ett mutex-objekt för att skydda en delad resurs från samtidig åtkomst av flera trådar eller processer. Varje tråd måste vänta på ägarskap för mutex innan den kan köra koden som kommer åt den delade resursen. Om flera trådar till exempel delar åtkomst till en databas kan trådarna använda ett mutex-objekt för att endast tillåta en tråd i taget att skriva till databasen.

I följande exempel används funktionen CreateMutex för att skapa ett mutex-objekt och funktionen CreateThread för att skapa arbetstrådar.

När en tråd i den här processen skriver till databasen begär den först ägarskap för mutex med hjälp av funktionen WaitForSingleObject. Om tråden erhåller äganderätten till mutexen, skriver den till databasen och frigör sedan sin äganderätt till mutexen med hjälp av funktionen ReleaseMutex.

I det här exemplet används strukturerad undantagshantering för att säkerställa att tråden släpper mutex-objektet korrekt. Det __finally kodblocket körs oavsett hur __try-blocket avslutas (såvida inte __try-blocket innehåller ett anrop till funktionen TerminateThread). Detta förhindrar att mutex-objektet överges oavsiktligt.

Om en mutex har övergivits har tråden som ägde mutexen inte korrekt släppt den innan den avslutades. I det här fallet är statusen för den delade resursen obestämd och om du fortsätter att använda mutex kan det dölja ett potentiellt allvarligt fel. Vissa program kan försöka återställa resursen till ett konsekvent tillstånd. Det här exemplet returnerar helt enkelt ett fel och slutar använda mutex. Mer information finns i Mutex-objekt.

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