Utilisation d’objets Mutex

Vous pouvez utiliser un objet mutex pour protéger une ressource partagée contre l’accès simultané par plusieurs threads ou processus. Chaque thread doit attendre la propriété du mutex avant de pouvoir exécuter le code qui accède à la ressource partagée. Par exemple, si plusieurs threads partagent l’accès à une base de données, les threads peuvent utiliser un objet mutex pour autoriser un seul thread à la fois à écrire dans la base de données.

L’exemple suivant utilise la fonction CreateMutex pour créer un objet mutex et la fonction CreateThread pour créer des threads de travail.

Lorsqu’un thread de ce processus écrit dans la base de données, il demande d’abord la propriété du mutex à l’aide de la fonction WaitForSingleObject . Si le thread obtient la propriété du mutex, il écrit dans la base de données, puis libère sa propriété du mutex à l’aide de la fonction ReleaseMutex .

Avertissement

Risque d’interblocage avec délai d’expiration INFINITE : L’exemple ci-dessous utilise INFINITE comme paramètre de délai d’attente pour WaitForSingleObject. Dans le code de production, cela signifie que le thread appelant reste bloqué indéfiniment si le thread propriétaire est victime d’un interblocage, se bloque ou ne parvient pas à libérer le mutex. Envisagez d’utiliser une valeur de délai d’attente finie et d’implémenter une logique de nouvelle tentative avec la journalisation des diagnostics pour détecter les blocages potentiels. Utilisez des outils tels que l’extension du débogueur !handle (par exemple) !handle <handle> fou Wait Chain Traversal (GetThreadWaitChain) pour diagnostiquer les interblocages mutex.

Cet exemple utilise la gestion structurée des exceptions pour s’assurer que le thread libère correctement l’objet mutex. Le bloc __finally de code est exécuté indépendamment de la façon dont le bloc __try se termine (sauf si le bloc __try inclut un appel à la fonction TerminateThread ). Cela empêche l’abandon par inadvertance de l’objet mutex.

Si un mutex est abandonné, le thread qui possédait le mutex ne l’a pas correctement libéré avant de terminer. Dans ce cas, l’état de la ressource partagée est indéterminé et la poursuite de l’utilisation du mutex peut masquer une erreur potentiellement grave. Certaines applications peuvent tenter de restaurer la ressource à un état cohérent ; cet exemple retourne simplement une erreur et cesse d’utiliser le mutex. Pour plus d’informations, consultez 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; 
}