Verwenden von Mutex-Objekten

Sie können ein Mutex-Objekt verwenden, um eine freigegebene Ressource vor gleichzeitigem Zugriff durch mehrere Threads oder Prozesse zu schützen. Jeder Thread muss auf den Besitz des Mutex warten, bevor er den Code ausführen kann, der auf die freigegebene Ressource zugreift. Wenn z. B. mehrere Threads den Zugriff auf eine Datenbank teilen, können die Threads ein Mutex-Objekt verwenden, um jeweils nur einen Thread zum Schreiben in die Datenbank zuzulassen.

Im folgenden Beispiel wird die CreateMutex-Funktion verwendet, um ein Mutex-Objekt und die CreateThread-Funktion zum Erstellen von Arbeitsthreads zu erstellen.

Wenn ein Thread dieses Prozesses in die Datenbank schreibt, fordert er zuerst den Besitz des Mutex mithilfe der WaitForSingleObject-Funktion an. Wenn der Thread den Besitz des Mutex erhält, schreibt er in die Datenbank und gibt dann den Besitz des Mutex mithilfe der ReleaseMutex-Funktion frei.

Warnung

Deadlock-Risiko bei INFINITE-Timeout: Das folgende Beispiel verwendet INFINITE als Timeout-Parameter für die Funktion WaitForSingleObject. Im Produktionscode bedeutet dies, dass der aufrufende Thread auf unbestimmte Zeit blockiert wird, wenn der Thread, der den Mutex besitzt, einen Deadlock erleidet, hängt oder den Mutex nicht freigibt. Erwägen Sie die Verwendung eines endlichen Timeoutwerts und implementieren Sie eine Wiederholungslogik mit diagnostischer Protokollierung, um potenzielle Deadlocks zu erkennen. Verwenden Sie Tools wie die !Handle-Debuggererweiterung (z. B. !handle <handle> f) oder Wait Chain Traversal (GetThreadWaitChain), um mutex deadlocks zu diagnostizieren.

In diesem Beispiel wird die strukturierte Ausnahmebehandlung verwendet, um sicherzustellen, dass der Thread das Mutex-Objekt ordnungsgemäß loslässt. Der __finally Codeblock wird unabhängig davon ausgeführt, wie der __try-Block beendet wird (es sei denn, der __try-Block enthält einen Aufruf der TerminateThread-Funktion ). Dadurch wird verhindert, dass das Mutex-Objekt versehentlich aufgegeben wird.

Wenn ein Mutex aufgegeben wurde, hat der Thread, dem der Mutex gehörte, ihn vor seiner Beendigung nicht ordnungsgemäß freigegeben. In diesem Fall ist der Status der freigegebenen Ressource nicht bestimmbar, und die weitere Verwendung des Mutex kann einen potenziell schwerwiegenden Fehler verschleiern. Einige Anwendungen versuchen möglicherweise, die Ressource in einem konsistenten Zustand wiederherzustellen; In diesem Beispiel wird einfach ein Fehler zurückgegeben und die Verwendung des Mutex beendet. Weitere Informationen finden Sie unter 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; 
}