Sincronización de Hilos POSIX: Variables de Condición

Las variables de condición son otro mecanismo de sincornización entre hilos. Las variables de condición usadas en conjunto con mutex permiten a un hilo esperar por la ocurrencia de una condición arbitraria. La espera es libre de carreras críticas.
Pars conseguir despertar cuando una condición de interés pueda cambiar,
En principio podríamos pensar que teniendo poseción de un mutex, podríamos consultar por el cambio de la variable que esperamos. Una vez hecha lacosulta deberíamos liberar el mutex. El problema es estamos obligados a hacer un loop esperando por el cambio de la variable. Esto es una espera activa (o espera ocupada) la cual ocupa mucha CPU.
Visibles a ambas hebras:
  pthread_mutex_t lock_de_mi_variable = PTHREAD_MUTEX_INITIALIZER;
  int variable = 0;

En una hebra:
   :
  int done = 0;
  while (!done) {  /*Espera activa u ocupada, indeseable */
     pthread_ mutex_lock(&lock_de_mi_variable);
       if ( varaible < MAXIMO)
             done=1;
      pthread_mutex_unlock(&lock_de_mi_variable);
  }
   /* hago lo que esperaba para variable < MAXIMO */
  :
En Otra hebra:
   :
   pthread_ mutex_lock(&lock_de_mi_variable);
   variable--;
   pthread_mutex_unlock(&lock_de_mi_variable);
   :


POSIX entrega una forma mejor de resolver este problema. La idea es que si la condición falla, en lugar de volver a consultar, quedarse bloqueado hasta que se nos informe de su cambio y así tenga sentido volver a consultar.  El recurso creado para esto se conoce como Variable de Condición. Éstas son representadas por el tipo de dato pthread_cond_t. Al igual que mutex, podemos iniciar su valor con la constante PTHREAD_COND_INITIALIZER. Si en lugar de cargar los atributos por omisión, podemos usar el llamado a la función pthread_cond_init.
#include <pthread.h>
int pthread_cond_init(pthread_cond_t *restrict cond, pthread_condattr_t *restrict attr);

int pthread_cond_destroy(pthread_cond_t *restrict cond);

Ambas retornan 0 si es OK, y un número de error en caso de falla.

Usamos la función pthread_cond_wait para esperar por el un cambio que dé sentido a evaluar nuevamente la condición. Existe una variante para limitar la espera a un cierto tiempo.
#include <pthread.h>
int pthread_cond_wait(pthread_cond_t *restrict cond, pthread_mutex_t *restrict mutex);

int pthread_cond_timewait(pthread_cond_t *restrict cond, pthread_mutex_t *restrict mutex, const struct timespec *restrict timeout);

Ambas retornan 0 si es OK, y un número de error en caso de falla.

Cuando un hilo invoca a pthread_cond_wait, éste libera el candado del mutex usado en el argumento. Más adelante cuando es despertado, éste candado es devuelto a la hebra.

Con estos llamados el problema se resuelve de la siguiente manera:
Visibles a ambas hebras:
  pthread_mutex_t lockDeMiVariable = PTHREAD_MUTEX_INITIALIZER;
  pthread_cond_t cambioDeVariable = PTHREAD_COND_INITIALIZER;
  int variable = 0;

En una hebra:
   :
  int done = 0;
  while (!done) {  
     pthread_ mutex_lock(&lockDeMiVariable);
       while ( variable >= MAXIMO))  /* llegó mi condición ?*/
            pthread_cond_wait(&cambioDeVariable, &lockDeMiVariable);
       done=1;
      pthread_mutex_unlock(&lockDeMiVariable);
  }
  /* hago lo que esperaba para variable < MAXIMO */
  :
En Otra hebra:
   :
   pthread_ mutex_lock(&lockDeMiVariable);
   variable--;
   pthread_mutex_unlock(&lockDeMiVariable);
   pthread_cond_signal(&cambioDeVariable);
   :

Hay dos funciones para notificar a hebras que una condición ha cambiado. La función pthread_cond_signal  despertará a una hebra que espera con la misma variable de condición. La función pthread_cond_broadcast despierta a todas la hebras esperando por la condición.

#include <pthread.h>
int pthread_cond_signal(pthread_cond_t *restrict cond);

int pthread_cond_broadcast(pthread_cond_t *restrict cond);

Ambas retornan 0 si es OK, y un número de error en caso de falla.

Un ejemplo: Cree un programa que envía a pantalla todo lo que se ingrese por pantalla. Para probar este programa y poder distinguir entre lo enviado por el programa y el eco automático del teclado, usted lo puede correr usando:
$ mkfifo myfifo
Luego en una consola pone:
$ cat myfifo
y en otra pone:
$ programaEco > myfifo

Ahora deseamos limitar a 1 caracter por segundo lo enviado a pantalla. Se usa el algoritmo leaky bucket con tamaño de balde de 10 caracteres. Usted puede ejecutar el programa como el previo y verá cómo los caracteres aparecen de a uno a la vez y con una tasa de 1 caracter/s.
Para controlar la tasa de tiempo, usted puede usar un timer iterativo o simplemente sleep(1). Usted puede estudiar ambos soluciones, ver:
 leakyBucket.c :  hace uso de SIGALRM, la cual requiere manejo de señales en hilos. Ésta es una solucion más compleja que,
 leakyBucket_with_sleep.c: Similar al anterior pero más simple.