Sincronización de hilos en POSIX: Mutex


Objetivos: Conocer los mecanismos de sincronización para acceso controlado a objetos compartidos.

Funciones POSIX para Sincronización
   Estas funciones incluyen mecanismos de exclusión mutua (mutex), mecanismos de señalización del cumplimiento de condiciones por parte de variables, y mecanismos de acceso de variables que se modifican en forma exclusiva, pero pueden ser leidas en forma compartida. Las funciones para el manejo de zonas de acceso exclusivo tienen el prefijo pthread_mutex
    Un mutex es una variable especial que puede tener estado tomado (locked) o libre (unlocked). Es como una compuerta que permite el acceso controlado. Si un hilo tiene el mutex entonces se dice que es el dueño del mutex. Si ningún hilo lo tiene se dice que está libre (o unclucked). Cada mutex tiene una cola de hilos que están esperando para tomar el mutex. El uso de mutex es eficiente, pero debería ser usado sólo cuando su acceso es solicitado por corto tiempo.

Función POSIX
Descripción
pthread_mutex_destroy
Destruye la variable (de tipo pthread_mutex_t) usada para manejo de explusión mutua, o candado mutes
pthread_mutex_init
permite dar las condiciones iniciales a un candado mutex
pthread_mutex_lock
Permite solicitar acceso al mutex, el hilo se bloquea hasta su obtención
pthread_mutex_trylock
permite solicitar acceso al mutex,  el hilo retorna inmediatamente. El valor retornado indica si otro hilo lo tiene.
pthread_mutex_unlock
Permite liberar un mutex.

Creación e iniciación de mutex

#include <pthread.h>
int  pthread_mutex_init(pthread_mutex_t * mutex, const pthread_mutexattr_t * attr);

Alternativamente podemos invocar:

pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;

lo cual inicializa el mutex con los atributos por omisión. Es el uso que daremos en este curso.
Es equivalente a invocar:
pthread_mutex_t mylock;
pthread_mutex_init(& mylock, NULL);

Destrucción de un mutex

#include <pthread.h>
int  pthread_mutex_destroy(pthread_mutex_t * mutex);

Si el mutex lo tenía otro hilo y éste es destruido, POSIX no define el comportamiento de mutex en esta situación.

Solicitud y liberación de un mutex

#include <pthread.h>
int  pthread_mutex_lock(pthread_mutex_t * mutex);
int  pthread_mutex_trylock(pthread_mutex_t * mutex);
int  pthread_mutex_unlock(pthread_mutex_t * mutex);

Con pthread_mutex_trylock el hilo siempre retorna, si la función es exitosa, se retorna 0 -como en los otros casos; si no se retornará EBUSY indicando que otro hilo tiene el mutex.

Ejemplo: Para protejer una zona crítica, usar:
pthread_mutex_t mylock = PTHREAD_MUTEX_INITIALIZER;

pthread_mutex_lock(&mylock);
/* Sección crítica */
pthread_mutex_unlock(&mylock);

Ejemplo:
Sin Mutex Con Mutex

int counter=0;

/* Function C */
void functionC()
{

counter++

}
/* Note scope of variable and mutex are the same */
pthread_mutex_t mutex1 = PTHREAD_MUTEX_INITIALIZER;
int counter=0;

/* Function C */
void functionC()
{
pthread_mutex_lock( &mutex1 );
counter++
pthread_mutex_unlock( &mutex1 );
}
Ejemplo: couter.c : conjunto de funciones para acceder a un contador a través de múltiples hilos.
Multiplicación de vectores sin threads y con threads.

Muchas llamados al sistema no son seguros de usar desde hilos (no son re-entrantes). strtok, rand, localtime, etc... Si necesitamos usar alguna de estas funciones, debemos considerarlas dentro de un código del tipo:
#include <pthread.h>
#include <stdlib.h>

int randsafe(double *ranp) {
static pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;
int error;

if (error = pthread_mutex_lock(&lock))
return error;
*ranp = (rand() + 0.5)/(RAND_MAX + 1.0);
return pthread_mutex_unlock(&lock);

}
Ejemplo: programa que calcula la suma de valores aleatorios de la función sin(), en varios hilos, los suma muestra su resultados.