Usar objetos de exclusión mutua
Puede usar un objeto de exclusión mutua para proteger un recurso compartido frente al acceso simultáneo por varios subprocesos o procesos. Cada subproceso debe esperar a la propiedad de la exclusión mutua para poder ejecutar el código que accede al recurso compartido. Por ejemplo, si varios subprocesos comparten acceso a una base de datos, los subprocesos pueden usar un objeto de exclusión mutua para permitir que solo un subproceso a la vez escriba en la base de datos.
En el ejemplo siguiente se usa la función CreateMutex para crear un objeto de exclusión mutua 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 de la exclusión mutua mediante la función WaitForSingleObject . Si el subproceso obtiene la propiedad de la exclusión mutua, escribe en la base de datos y, a continuación, libera su propiedad de la exclusión mutua mediante la función ReleaseMutex .
En este ejemplo se usa el control estructurado de excepciones para asegurarse de que el subproceso libera correctamente el objeto de exclusión mutua. El bloque __finally de código se ejecuta independientemente de cómo finalice el bloque de __try (a menos que el bloque __try incluya una llamada a la función TerminateThread ). Esto evita que el objeto de exclusión mutua se abandone accidentalmente.
Si se abandona una exclusión mutua, el subproceso que tenía la exclusión mutua no lo liberó correctamente antes de terminar. En este caso, el estado del recurso compartido es indeterminado y seguir usando la exclusión mutua puede ocultar un error potencialmente grave. Algunas aplicaciones podrían intentar restaurar el recurso a un estado coherente; este ejemplo simplemente devuelve un error y deja de usar la exclusión mutua. 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;
}