Continuando a série de posts sobre problemas IPC (Inter-Process Communication), apresentamos agora o problema do "Barbeiro Dorminhoco", acompanhado por uma uma solução que utiliza threads e semáforos. Finalmente, testes são apresentados, comprovando o bom funcionamento de nossa solução.
A situação é bem simples: imagine uma salão de cabelereiros onde, após demissões em massa, apenas o barbeiro-chefe trabalha, atendendo aos clientes que chegam.
Sempre que o expediente está tranquilo demais..... o barbeiro adormece! Este só volta a acordar quando um novo cliente chega, ansioso por um corte.
CÓDIGO-FONTE
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <pthread.h>
#include <semaphore.h>
#include <errno.h>
#define N 20
#define CHAIRS 5
#define MAX_CUT_TIME 5
#define MAX_TIME_BEFORE_START 8
sem_t *customers, *barbers;
pthread_mutex_t mutex;
int waiting = 0;
int customer_id[N];
int barber_id;
char* customer_name[20] =
{"A","B","C","D","E", "F","G","H","I","J","K","L","M","N","O","P","Q","R","S","T"};
void *barber(void *arg);
void *customer(void *arg);
void cut_hair();
void get_haircut(int i);
void sem_close_n_unlink(sem_t *semId, char *semName);
void error_exit(const char*);
char *rand_str(char *mystr, int size);
int main() {
srand(time(NULL));
int res;
pthread_t barber_thread, cust_thread[N];
void *thread_result;
int index;
for (index = 0; index < N; index++) {
customer_id[index] = index;
}
barber_id = 0;
//iniciar mutex
res = pthread_mutex_init(&mutex, NULL);
if (res != 0) error_exit("Inicializacao do mutex falhou");
// iniciar semaforos
// atencao: em sistemas darwin, utilizamos sem_open ao inves de sem_init
// pois no Mac OS X, apenas *semaforos nomeados* estao implementados.
char *barbersem , *customersem;
barbers = sem_open(rand_str(barbersem,8),O_CREAT,0,0);
customers = sem_open(rand_str(customersem,8),O_CREAT,0,0);
// criar threads
res = pthread_create(&barber_thread, NULL, barber, (void *)&barber_id);
if (res != 0) error_exit("Criacao de thread falhou");
for (index = 0; index < N; index++) {
res = pthread_create(&cust_thread[index],NULL,customer,(void *)&customer_id[index]);
if (res != 0) error_exit("Criacao de thread falhou");
}
res = pthread_join(barber_thread,&thread_result);
if (res != 0) error_exit("Falhou quando tentou pthread_join");
//destroy mutex
pthread_mutex_destroy(&mutex);
// closing and unlink semaphores
sem_close_n_unlink(barbers, barbersem);
sem_close_n_unlink(customers, customersem);
return 1;
}
void *barber(void *arg){
int id = *(int *) arg;
while (1) {
printf("Barbeiro desocupado.\n");
sem_wait(customers);
printf("== Fila de espera tem %d clientes.\n", waiting);
pthread_mutex_lock(&mutex);
waiting = waiting - 1;
sem_post(barbers);
printf("Barbeiro pronto para cortar algum cabelo.\n");
pthread_mutex_unlock(&mutex);
cut_hair();
}
}
void *customer(void *arg) {
int id = *(int *) arg;
sleep(rand() % MAX_TIME_BEFORE_START); //simular o comportamento de chegada dos clientes
pthread_mutex_lock(&mutex);
if (waiting < CHAIRS) {
waiting = waiting + 1;
sem_post(customers);
printf("%s esta na fila de espera. Tamanho da fila eh %d.\n", customer_name[id], waiting);
pthread_mutex_unlock(&mutex);
sem_wait(barbers);
get_haircut(id);
}
else {
printf(" Cliente %s passando direto. Fila cheia!\n", customer_name[id]);
pthread_mutex_unlock(&mutex);
}
pthread_exit("customer exits.\n");
}
void cut_hair() {
sleep(3);
}
void get_haircut(int i) {
printf("%s esta cortando o cabelo\n", customer_name[i]);
sleep(3);
}
void error_exit(const char *msg) {
perror(msg);
exit(EXIT_FAILURE);
}
char *rand_str(char *mystr, int size) {
if ((mystr = calloc(size + 1, sizeof(char))) == NULL) {
printf("callor error.\n");
return NULL;
}
int aux;
for (aux = 0; aux < size ; aux++) {
mystr[aux] = (char)((rand() % 23) + 'a');
}
mystr[aux] = '\0';
return mystr;
}
void sem_close_n_unlink(sem_t *semId, char *semName) {
if (sem_close (semId) == -1) {
printf ("sem_close failed\n");
return;
}
if (sem_unlink (semName) == -1) {
printf ("sem_unlink failed\n");
return;
}
printf ("closed and unlinked semaphore\n");
}
TESTES
Abaixo, apresentamos a saída obtida durante execução do problema. Observeque, de fato, ela é consistente com as restrições impostas pelo enunciado do
problema.
Barbeiro desocupado.
F esta na fila de espera. Tamanho da fila eh 1.
H esta na fila de espera. Tamanho da fila eh 2.
Q esta na fila de espera. Tamanho da fila eh 3.
== Fila de espera tem 3 clientes.
Barbeiro pronto para cortar algum cabelo.
F esta cortando o cabelo
D esta na fila de espera. Tamanho da fila eh 3.
N esta na fila de espera. Tamanho da fila eh 4.
I esta na fila de espera. Tamanho da fila eh 5.
Cliente J passando direto. Fila cheia!
Cliente C passando direto. Fila cheia!
Cliente K passando direto. Fila cheia!
Cliente P passando direto. Fila cheia!
Barbeiro desocupado.
== Fila de espera tem 5 clientes.
Barbeiro pronto para cortar algum cabelo.
H esta cortando o cabelo
A esta na fila de espera. Tamanho da fila eh 5.
Cliente E passando direto. Fila cheia!
Cliente S passando direto. Fila cheia!
Cliente T passando direto. Fila cheia!
Cliente L passando direto. Fila cheia!
Cliente O passando direto. Fila cheia!
Cliente R passando direto. Fila cheia!
Barbeiro desocupado.
== Fila de espera tem 5 clientes.
Barbeiro pronto para cortar algum cabelo.
Q esta cortando o cabelo
No meu caso tenho um problema que é uma loja em que o cliente depois de ser atendido se desloca às caixas de pagamento e efectua o pagamento e de seguida desloca-se para o armazem para levantar os produtos.
ReplyDeleteComo posso implementar estes dois casos de deslocamento?
A minha outra duvida é como podemos definir o 'waiting'?
Cumps
Diagrama de actividades
ReplyDelete