#include <stdio.h>
#include <pthread.h>
pthread_mutex_t MyMutex = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t seguir2 = PTHREAD_COND_INITIALIZER;
pthread_cond_t seguir = PTHREAD_COND_INITIALIZER;
#define N 5
int aloja = 0;
int sigo = 0;
/*
Cuando un thread -que debe tener el mutex ya tomado- llama a wait(), suelta el mutex y
entra en espera bloqueante.
DEBE TENER EL MUTEX YA TOMADO!
Cuando un thread llama a signal(), otro thread en espera, de haberlo, se despierta de su wait()
CON EL MUTEX YA ADQUIRIDO
CON EL MUTEX YA ADQUIRIDO!
*/
void *freno (void *a)
{
for (;;){
// Tomo el mutex.
pthread_mutex_lock(&MyMutex);
// Hey!, quiero entrar, terminá lo que estás haciendo, te espero.
aloja = 1;
// Espero hasta que se "apague" la función avanzar.
// Cuando se apaga, AUTOMATICAMENTE TOMO YO EL MUTEX!!
while (sigo != 0){printf ("me llego la señal.\n"); pthread_cond_wait(&seguir,&MyMutex);}
// Si llegué hasta acá es porque yo tengo el mutex nadie mas lo puede tomar.
printf ("Estoy en freno\n");
// Terminé de hacer todo lo que quería hacer.
aloja = 0; // Por más que lo ponga el 0, "avanzar" no puede entrar todavía, porque
// yo tengo el mutex!
/* Libero el mutex, es decir, ahora la función avanzar sí puede tomar el mutex, PERO
a la vez no ya que está dentro de un wait esperando un SIGNAL no que libere un mutex.
Aunque no está demás recordar que solo 1 puede tomar el mutex a la vez. */
pthread_mutex_unlock(&MyMutex);
/*
Cuando un thread llama a signal(), otro thread en espera, de haberlo, se despierta de su wait
CON EL MUTEX YA TOMADO!
pthread_cond_signal(&seguir2);
*/
/*
Cuando un thread llama a broadcast(), todos los threads en espera, de haberlo, se despiertan de su
wait CON EL MUTEX YA TOMADO, es decir en este caso, se pelean por quien toma el mutex.
*/
pthread_cond_broadcast(&seguir2);
}
}
void *avanzar (void *b)
{
for (;;){
// Tomo yo el mutex, nadie más lo puede tomar.
pthread_mutex_lock(&MyMutex);
sigo++;
// Libero, ahora si puede tomar el mutex la función "freno".
pthread_mutex_unlock(&MyMutex);
//sleep(3);
printf ("estoy en sigo.\n");
// No puedo tomar el mutex hasta que la función freno recién llegue a la linea donde entra en espera!
pthread_mutex_lock(&MyMutex);
sigo--;
// Libero el mutex, recien ahora pueden ejecutarse más funciones "freno", antes no porque yo tenía el
// mutex tomado.
pthread_mutex_unlock(&MyMutex);
printf("Avanzar: %d\n",sigo);
/* Esta señal está esperando "freno" para poder continuar, pero además de esperar la señal espera
también que la cantidad de "sigo" sea igual a 0 */
/*
Si sigo es un número mayor a 0, entonces de todos modos va a seguir esperando.
*/
pthread_cond_signal(&seguir);
/*PERO! a la vez yo me bloqueo. Esperando ahora que "freno" me desbloque. */
/* Y a su vez freno está esperando que yo lo desbloquee. */
while (aloja != 0)pthread_cond_wait(&seguir2,&MyMutex);
/*
¿Qué pasaría si avanzar toma el mutex e incrementa en 1 la variable "sigo" y al toque "freno" pone
en 1 la variable aloja?
Lo que pasaría es que la variable sigo se incrementa luego suelta el mutex, lo toma la función"freno"
pone en 1 aloja pero como sigo es mayor que cero entonces SUELTA el mutex automáticamente y deja
que siga funcionando "avanzar", entonces pueden pasar cosas!:
Primero de todo, no se pueden ejecutar dos "frenos" a a la vez pero si se pueden ejecutar "avanzar"
muchas veces.
Entonces, avanzar toma el mutex y decrementa en 1 la variable
sigo y al momento lo suelta, PERO "freno" todavía no puede hacer nada porque está esperando un
signal, no está esperando que suelte el mutex (explicado anteriormente)
Entonces yo le doy el signal y suponiendo que el numero de sigo es igual a 0, se ejecuta freno y yo
entro en espera hasta que freno termine.
*/
}
}
int main (void)
{
int i;
pthread_t prueba1[N],prueba2[N];
for (i=0; i<N; i++){
pthread_create(&prueba1[i],0,freno,"aloja");
pthread_create(&prueba2[i],0,avanzar,"chau");
}
pthread_join(prueba1[0],0);
return 0;
}
El programa sigue está logica aunque las funciones no se llamen lector y escritor.
1. No puede haber un lector accediendo al arreglo al mismo tiempo que un escritor.
2. Varios lectores pueden acceder al arreglo simult´aneamente.
3. S´olo puede haber un escritor a la vez.
Y además cuando los lectores que están leyendo terminen su lectura pero no se puedan empezar nuevas lecturas hasta que el escritor escriba.
freno = escritor.
avanzar = lector.
Aunque en este caso no escriben ni leen nada solo muestran texto.
El problema es que no funciona y no sé que es lo que estoy pensando mal, el programa se congela es decir en algún momento queda esperando algo que nunca llega.
Un detalle importante: Ponle mutex a los printf.
Y luego pues... Intenta darle un enfoque práctico al programa. A ti te puede ser más fácil visualizarlo así, y desde luego, a nosotros también (decir que es como un lector y un escritor no ayuda mucho).
Especifica qué quieres que haga el programa exactamente, y entonces será más fácil verlo.
Lo solucioné así:
#include <stdio.h>
#include <pthread.h>
#define N 15
#define ARRLEN 1024
pthread_mutex_t MyMutex = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t seguir = PTHREAD_COND_INITIALIZER;
pthread_cond_t seguir2 = PTHREAD_COND_INITIALIZER;
int arr[ARRLEN];
/* Cantidad de funciones "lectores" que se están ejecutando.*/
int lectores = 0;
/* Variable utilizada por si quiero escribir.*/
int wannawrite = 0;
void *escritor(void *arg)
{
int i;
int num = *((int *)arg);
for (;;) {
/*
Escribo el mutex al principio y este está tomando toda la función porque no pueden
haber más de 1 escritor a la vez.
*/
pthread_mutex_lock(&MyMutex);
/* Hey!!, quiero escribir!!, ahora todos deben dormir hasta que termine de escribir.*/
wannawrite = 1;
/* Espero hasta que todos los que estan leyendo, terminen de leer y se pogan a dormir
De no ser así libero el mutex.
wait(): Si tiene la señal, entonces despierta con EL MUTEX YA ADQUIRIDO.
*/
while (lectores != 0) pthread_cond_wait(&seguir,&MyMutex);
/* sleep(random()%3); */
for (i=0; i<ARRLEN; i++) {
arr[i] = num;
}
/* Terminé de escribir. */
wannawrite = 0;
/* Heeyy!!, despertá todos los lectores que tuvieron que dormir para que pueda escribir.*/
pthread_cond_broadcast(&seguir2);
pthread_mutex_unlock(&MyMutex);
}
return NULL;
}
void *lector(void *arg)
{
int v, i, err;
int num = *((int *)arg);
for (;;) {
/* En este caso si pueden haber más lectores a la vez entonces no voy a bloquear toda la función con un mutex,
solo la bloquearé por partes. */
pthread_mutex_lock(&MyMutex);
lectores++;
pthread_mutex_unlock(&MyMutex);
/* sleep(random()%3); */
err = 0;
v = arr[0];
for (i=1; i<ARRLEN; i++) {
if (arr[i]!=v) {
err=1;
break;
}
}
if (err) printf("Lector %d, error de lectura\n", num);
else printf("Lector %d, dato %d\n", num, v);
pthread_mutex_lock(&MyMutex);
lectores--;
/* si pondría un unlock acá, podría pasar cualquier cosa hasta que mande
la señal de que yo terminé de leer y luego esperar. Por eso el unlock lo pongo al final.
Si yo tengo el mutex por más que el otro tenga la señal y los lectores sean 0
no puede tomar el mutex por lo tanto se queda bloqueado hasta que lo libero. */
/* Mando una señal que un thread terminó de leer. */
pthread_cond_signal(&seguir);
/* Si quiere escribir, entonces me bloqueo hasta que me avise que terminó de escribir.*/
while(wannawrite == 1) pthread_cond_wait(&seguir2,&MyMutex);
/* Ahora si suelto el mutex.*/
pthread_mutex_unlock(&MyMutex);
}
return NULL;
}
int main()
{
int i;
pthread_t lectores[N], escritores[N];
int arg[N];
for (i=0; i<ARRLEN; i++) {
arr[i] = -1;
}
for (i=0; i<N; i++) {
arg[i] = i;
pthread_create(&lectores[i], NULL, lector, (void *)&arg[i]);
pthread_create(&escritores[i], NULL, escritor, (void *)&arg[i]);
}
pthread_join(lectores[0], NULL); /* Espera para siempre */
return 0;
}
De todos modos esta solución le da demasiada prioridad a los escritores que a los lectores, no sé por qué se debe eso.
¿Alguna idea?