¡Esta es una revisión vieja del documento!
Semáforos
Un semáforo es un contenedor de tickets. En él se pueden depositar un ticket con sem_post y extraer un ticket con sem_wait. La garantía es que si sem_wait no encuentra tickets entonces no retorna hasta que algún thread invoque sem_post para depositar un ticket.
Un semáforo se manipula con las siguientes funciones:
#include <semaphore.h> sem_t sem; sem_init(&sem, 0, tickets_iniciales); sem_post(&sem); sem_wait(&sem);
La siguiente es una implementación correcta de la estructura Buffer del productor/consumidor.
/* Versión correcta */
typedef struct {
int size;
Cuadro **array;
int in, out;
sem_t empty, full;
} Buffer;
Buffer *nuevoBuffer(int size) {
Buffer *buf= (Buffer*)malloc(sizeof(Buffer));
buf->size= size;
buf->array= (Cuadro**)malloc(size*sizeof(Cuadro*));
buf->in= buf->out= 0;
sem_init(&buf->empty, 0, size);
sem_init(&buf->full, 0, 0);
return buf;
}
void put(Buffer *buf, Cuadro *cuadro) {
sem_wait(&buf->empty);
buf->array[buf->in]= cuadro;
buf->in= (buf->in+1) % buf->size;
sem_post(&buf->full);
}
Cuadro *get(Buffer *buf) {
Cuadro *cuadro;
sem_wait(&buf->full);
cuadro= buf->array[buf->out];
buf->out= (buf->out+1) % buf->size;
sem_post(&buf->empty);
return cuadro;
}
Ejercicio: múltiples productores y múltiples consumidores
Hay muchos problemas que se pueden paralelizar usando la abstracción productor/consumidor. En ellos la solución es la misma de arriba. Solo cambia el código del productor, del consumidor y los ítemes que se depositan en el Buffer. Pero el código del buffer es el mismo.
El problema del productor/consumidor se puede generalizar a múltiples productores y múltiples consumidores que comparten el mismo buffer. Muestre que en este caso el código para depositar y extraer elementos de buffer puede sufrir de data-races. Corrija el código usando un semáforo o mutex adicional.
Segunda solución correcta y eficiente de la cena de filósofos
La segunda solución se basa en que el deadlock solo se produce si los 5 filósofos llegan a cenar. Si se limita a 4 los filósofos que pueden cenar, ya no habrá deadlock. Entonces se usa un semáforo con 4 tickets iniciales:
pthread_mutex_t palitos[5];
sem_t portero; /* con 4 tickets iniciales */
void filosofo(int i) {
for (;;) {
sem_wait(&portero);
pthread_mutex_lock(&palitos[i]);
pthread_mutex_lock(&palitos[(i+1)%5]);
comer(i, (i+1)%5);
pthread_mutex_unlock(&palitos[i]);
pthread_mutex_unlock(&palitos[(i+1)%5]);
sem_post(&portero);
pensar();
}
}
El problema de esta solución es que solo se aplica a 5 filósofos y 5 palitos. No se puede generalizar a un número variable de threads accediendo a múltiples estructuras de datos.
