Uso de objetos de exclusión mutua

Puede usar un objeto mutex para proteger un recurso compartido del acceso simultáneo por varios subprocesos o procesos. Cada hilo debe esperar a adquirir el mutex antes de poder ejecutar el código que accede al recurso compartido. Por ejemplo, si varios subprocesos comparten el acceso a una base de datos, estos pueden usar un objeto mutex para permitir que solo un subproceso escriba en la base de datos cada vez.

En el ejemplo siguiente se usa la función CreateMutex para crear un objeto mutex y la función CreateThread para crear subprocesos de trabajo.

Cuando un subproceso de este proceso escribe en la base de datos, primero solicita la propiedad del mutex mediante la función WaitForSingleObject. Si el hilo adquiere la propiedad del mutex, escribe en la base de datos y, a continuación, libera la propiedad del mutex mediante la función ReleaseMutex.

Warning

Riesgo de interbloqueo con tiempo de espera INFINITO: En el ejemplo siguiente se usa INFINITE como parámetro de tiempo de espera para WaitForSingleObject. En el código de producción, esto significa que el hilo que llama se bloqueará indefinidamente si el hilo propietario entra en interbloqueo, se cuelga o no libera el mutex. Considere la posibilidad de usar un valor de tiempo de espera finito e implementar la lógica de reintento con el registro de diagnóstico para detectar posibles interbloqueos. Utilice herramientas como la extensión del depurador !handle (por ejemplo, !handle <handle> f) o el recorrido de la cadena de espera (GetThreadWaitChain) para diagnosticar interbloqueos de mutex.

Este ejemplo usa el manejo estructurado de excepciones para garantizar que el subproceso libere correctamente el objeto de exclusión mutua. El bloque __finally de código se ejecuta independientemente de cómo finalice el bloque __try (a menos que el bloque __try incluya una llamada a la función TerminateThread ). Esto impide que el objeto de exclusión mutua se abandone involuntariamente.

Si se abandona un mutex, el subproceso propietario del mutex no lo liberó correctamente antes de finalizar. En este caso, el estado del recurso compartido es indeterminado y seguir usando el mutex puede ocultar un error potencialmente grave. Algunas aplicaciones podrían intentar restaurar el recurso a un estado coherente; en este ejemplo simplemente se devuelve un error y se deja de usar el mutex. Para obtener más información, vea Objetos de exclusión mutua.

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