Introducción al multiprocesamiento en C++ IV: sincronización con Mutex
Oscar Campos
Después de un breve descanso veraniego, hoy vamos a continuar con la serie introducción al multiprocesamiento en C++. En esta cuarta parte vamos a crear un wrapper alrededor de algunos mecanismos de sincronización.
Más específicamente vamos a crear una clase alrededor de la primitiva de sincronización mutex que ya vimos en el primer artículo de la serie.
Para nuestro wrapper sencillo alrededor de los threads vamos a utilizar solo mutexes por que creo que es lo único que vamos a necesitar.
GThreads con Mutex
Windows tiene una estructura propia llamada mutex
pero no vamos a hacer uso de ellos en nuestra clase. Los objetos mutex
de Windows pueden ser usados entre procesos y son bastante pesados.
Para nuestro cometido no vamos a necesitar la funcionalidad adicional que ofrece ese tipo de objeto así que vamos a implementar una clase de mutex
ligera cuyos objetos puedan ser usados entre diferentes threads pero no entre procesos.
En Windows vamos a utilizar el objeto ligero critical section que es precisamente un mutex
que cubre perfectamente nuestras necesidades.
El Mutex
Vamos a crear un nuevo archivo llamado gmutex.h (tech.lat Mutex) y vamos a escribir una sencilla clase que sirva como wrapper alrededor de las estructuras pthread_mutex_t
en Unix y CRITICAL_SECTION
en Windows.
Para ello vamos a hacer uso como siempre de la compilación condicional a la que ya estamos acostumbrados, vamos a escribir un prototipo de nuestra clase:
class GMutex {protected: // Tipos de mutex #ifdef WIN32 CRITICAL_SECTION m_mutex; #else pthread_mutex_t m_mutex; #endif};
El Constructor
En ambas APIs es necesario que el objeto mutex
sea iniciado antes de poder utilizarlo, así que vamos a hacerlo en el constructor:
GMutex(){ #ifdef WIN32 InitializeCriticalSection( &m_mutex ); #else phtread_mutex_init( &m_mutex, 0 ); #endif}
El segundo argumento en la función para Unix es supuéstamente un puntero a una estructura de configuración para el mutex
pero no es muy necesario. Al pasar el valor 0 los atributos por defecto son aplicados.
Destructor
Ambas APIs requieren de la destrucción del mutex
cuando hemos terminado de usarlos. Lo lógico es añadirlo al destructor de la clase:
~GMutex(){ #ifdef WIN32 DeleteCriticalSection( &m_mutex ); #else pthread_mutex_destroy( &m_mutex ); #endif}
Adquirir el Mutex
Adquirir un mutex
es una tarea sencilla con ambas APIs:
inline void Lock(){ #ifdef WIN32 EnterCriticalSection( &m_mutex ); #else pthread_mutex_lock( &m_mutex ); #endif}
Liberar el Mutex
Liberarlo es igual de sencillo:
inline void Unlock(){ #ifdef WIN32 LeaveCriticalSection( &m_mutex ); #else pthread_mutex_unlock( &m_mutex ); #endif}
Código completo
El código completo de la clase es el siguiente:
#ifndef _GMutex_h_
define _GMutex_h@#@
ifdef WIN32
#include <windows.h>
else
#include <pthreads.h>
endif
namespace ThreadLib{ class GMutex { public: GMutex() { #ifdef WIN32 InitializeCriticalSection( &m_mutex ); #else pthread_mutex_init( &m_mutex ); #endif }
~GMutex() { #ifdef WIN32 DeleteCriticalSection( &m_mutex ); #else pthread_mutex_destroy( &m_mutex ); #endif } inline void Lock() { #ifdef WIN32 EnterCriticalSection( &m_mutex ); #else pthread_mutex_lock( &m_mutex ); #endif } inline void Unlock() { #ifdef WIN32 LeaveCriticalSection( &m_mutex ); #else pthread_mutex_unlock( &m_mutex ); #endif }protected: #ifdef WIN32 CRITICAL_SECTION m_mutex; #else pthread_mutex_t m_mutex; #endif}
}
endif // _GMutex_h_
Como podéis comprobar, nos ha quedado una clase muy sencilla. En el próximo artículo veremos como utilizar esta librería en una demo sencilla.
En tech.lat Dev | Introducción al multiprocesamiento en C++