Использование объектов мьютекса
Объект мьютекса можно использовать для защиты общего ресурса от одновременного доступа нескольких потоков или процессов. Каждый поток должен дождаться владения мьютексом, прежде чем он сможет выполнить код, который обращается к общему ресурсу. Например, если несколько потоков совместно используют доступ к базе данных, потоки могут использовать объект мьютекса, чтобы разрешить запись в базу данных только одному потоку за раз.
В следующем примере используется функция CreateMutex для создания объекта мьютекса и функция CreateThread для создания рабочих потоков.
Когда поток этого процесса записывает данные в базу данных, он сначала запрашивает владение мьютексом с помощью функции WaitForSingleObject . Если поток получает право владения мьютексом, он записывается в базу данных, а затем освобождает владение мьютексом с помощью функции ReleaseMutex .
В этом примере используется структурированная обработка исключений для обеспечения правильного выпуска объекта мьютекса потоком. __finally блок кода выполняется независимо от того, как завершается блок __try (если блок __try не включает вызов функции TerminateThread). Это предотвращает случайное прерывание объекта мьютекса.
Если мьютекс был прерван, поток, владеющий мьютексом, не отпустил его должным образом перед завершением. В этом случае состояние общего ресурса неопределенное, и продолжение использования мьютекса может скрыть потенциально серьезную ошибку. Некоторые приложения могут попытаться восстановить ресурс в согласованном состоянии; Этот пример просто возвращает ошибку и прекращает использование мьютекса. Дополнительные сведения см. в разделе Объекты мьютекса.
#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;
}