Поделиться через


Использование объектов Mutex

Можно использовать объект мьютекса для защиты общего ресурса от одновременного доступа несколькими потоками или процессами. Каждый поток должен дождаться получения мьютекса, прежде чем он сможет выполнить код, чтобы обратиться к общему ресурсу. Например, если несколько потоков совместно используют доступ к базе данных, потоки могут использовать объект мьютекса, чтобы разрешить одновременно записывать в базу данных только один поток.

В следующем примере используется функция CreateMutex для создания объекта мьютекса и функция CreateThread для создания рабочих потоков.

Когда поток этого процесса записывается в базу данных, он сначала запрашивает владение мьютексом с помощью функции WaitForSingleObject. Если поток получает владение мьютексом, он записывает данные в базу, а затем освобождает владение мьютексом с помощью функции ReleaseMutex.

В этом примере используется структурированная обработка исключений, чтобы поток правильно выпускал объект мьютекса. Блок __finally кода выполняется независимо от того, как завершается блок __try (если __try блок не включает вызов функции TerminateThread). Это предотвращает ненамеренное оставление объекта мьютекса.

Если мьютекс заброшен, поток, принадлежащий мьютексу, не выпустил его должным образом перед завершением. В этом случае состояние общего ресурса является неопределенным, и продолжение использования мьютекса может скрыть потенциально серьезную ошибку. Некоторые приложения могут попытаться восстановить ресурс в согласованном состоянии; этот пример просто возвращает ошибку и останавливает использование мьютекса. Для получения более подробной информации см. в разделе объекты типа Mutex.

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