PTHREADS - Programación concurrente.

Iniciado por GGZ, 6 Mayo 2017, 21:21 PM

0 Miembros y 1 Visitante están viendo este tema.

GGZ

#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.

LET'S DO STUFF!!

ivancea96

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.

GGZ

#2
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?
LET'S DO STUFF!!