Udostępnij za pośrednictwem


Korzystanie z obiektów Mutex

Można użyć obiektu mutex , aby chronić udostępniony zasób przed równoczesnym dostępem przez wiele wątków lub procesów. Każdy wątek musi czekać na dostęp do mutexu, zanim będzie mógł wykonać kod, który ma dostęp do udostępnionego zasobu. Jeśli na przykład kilka wątków dzieli dostęp do bazy danych, wątki mogą używać obiektu mutex, aby zezwolić tylko jednemu wątkowi na zapis do bazy danych.

W poniższym przykładzie użyto funkcji CreateMutex, aby utworzyć obiekt mutex i funkcję CreateThread do tworzenia wątków roboczych.

Gdy wątek tego procesu zapisuje w bazie danych, najpierw żąda przejęcia własności mutexa przy użyciu funkcji WaitForSingleObject. Jeśli wątek uzyskuje kontrolę nad mutexem, zapisuje do bazy danych, a następnie zwalnia ją przy użyciu funkcji ReleaseMutex.

W tym przykładzie użyto obsługi wyjątków strukturalnych, aby upewnić się, że wątek prawidłowo zwalnia obiekt mutex. Blok __finally kodu jest wykonywany niezależnie od sposobu zakończenia bloku __try (chyba że blok __try zawiera wywołanie funkcji TerminateThread). Zapobiega to ryzyku nieumyślnego porzucenia obiektu mutex.

Jeśli mutex został porzucony, wątek, który go posiadał, nie zwolnił go poprawnie przed zakończeniem. W takim przypadku stan zasobu udostępnionego jest nieokreślony i dalsze korzystanie z mutexu może ukryć potencjalnie poważny błąd. Niektóre aplikacje mogą próbować przywrócić zasób do spójnego stanu; Ten przykład po prostu zwraca błąd i przestaje używać mutexu. Aby uzyskać więcej informacji, zobacz 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; 
}