Programação de Sistemas Cafetaria Programação de Sistemas Cafetaria : 1/14 Problema Uma cafetaria vende duas classes de café, cada um servido por uma fila dedicada: local e levar ( take away ). Ao entrar na loja, o cliente declara a sua preferência de prova da bebida estimulante: local, levar ou indiferente. O cliente é dirigido para a fila da sua preferência, excepto os indiferentes que são colocados na fila com menos clientes. Um café demora sempre 10 segundos a ser tirado. Depois de ter sido atendido, o cliente que toma localmente o café sai da loja fim de 6 segundos, o cliente servido no posto Levar sai imediatamente da loja. Cada empregado (unidade funcional) serve o cliente enquanto a sua fila tiver clientes. Quer no início do dia de trabalho, quer na altura em que a sua fila se encontrar vazia, o empregado descansa. Os postos fecham logo que o último cliente seja atendido. Programação de Sistemas Cafetaria : 2/14
Estratégia 1. Clientes e postos de serviço implementados por fios de execução cliente e posto. 1. cliente tem como argumento o seu ídentificador (int). 2. posto tem como argumentos o tipo de posto, o semáforo de espera do cliente e o mutex de sincronização da entrega do café ao cliente. 2. Semáforos filalocal e filalevar, inicializados a 0, asseguram os clientes serem atendidos em fila nos postos seleccionados. 3. Variáveis numblocal e numblevar, de acesso protegido pelo mutex chave, indicam os comprimentos das filas. 4. A variável aservir determina o número de clientes que falta atender (quando chegar a 0, a loja fecha). O posto que atende o último cliente envia um sinal ao outro posto, para que termine. 5. Sincronização da entrega do café, entre posto e cliente, assegurada pelos mutexes entregalocal e entregalevar. Programação de Sistemas Cafetaria : 3/14 Programa tipos de dados #include <pthread.h> #include <unistd.h> #include <semaphore.h> #include <signal.h> #include <stdio.h> #include <stdlib.h> #define CUSTOMERS 30 /* numero máximo de clientes */ typedef enum { opcaolocal,opcaolevar,opcaoindiferente } seleccao; typedef struct{ seleccao this; /* tipo de posto */ sem_t *fila; /* semáforo onde cliente espera a sua vez */ pthread_mutex_t *entrega; /* mutex de entrega do café */ } argumentsp; typedef struct{ int id; /* Id do cliente */ }argumentsc; Programação de Sistemas Cafetaria : 4/14
Programa:variáveis globais e handler sem_t filalocal, /* semáforo para posto de café local */ filalevar; /* semaforo para posto de café para levar */ pthread_mutex_t entregalocal, /* entrega do café local */ entregalevar, /* entrega do café para levar */ chave; pthread_t localtid,levartid; unsigned numblocal, numblevar, aservir; void tratamento( int signumb ) { if( signumb==sigusr1 ) pthread_exit( NULL ); } Programação de Sistemas Cafetaria : 5/14 Programa posto void *posto(void *args) { seleccao this = ((argumentsp *)args)->this; sem_t *fila = ((argumentsp *)args)->fila; pthread_mutex_t *entrega = ((argumentsp *)args)->entrega; } signal( SIGUSR1,tratamento ); pthread_mutex_lock( entrega ); for( ;; ) { sem_wait( fila ); /* espera proximo cliente */ printf("caixa %s prepara cafe\n",this==opcaolocal? local : levar ); sleep( 10 ); printf("caixa %s entrega cafe\n",this==opcaolocal? local : levar ); pthread_mutex_unlock( entrega ); if( this==opcaolocal ) numblocal--; if( this==opcaolevar ) numblevar--; pthread_mutex_unlock( &chave ); } Programação de Sistemas Cafetaria : 6/14
Programa cliente (1) void *cliente( void *args ) { arguments *a=(arguments *)args; int choice = rand()%3; seleccao opcao; printf("[cliente %d]: seleccionou ",a->id); if ( choice==0 ) { opcao=opcaolocal; printf("local\n"); } if ( choice==1 ) { opcao=opcaolevar; printf("levar\n"); } if ( choice==2 ) { opcao=opcaoindiferente; printf("indiferente-"); } if ( opcao==opcaoindiferente ) { printf( "{local=%d,levar=%d}:",numblocal,numblevar ); if( numblevar>numblocal ) { printf( "colocado na caixa Local\n ); opcao=opcaolocal; } else { printf( "colocado na caixa Levar\n ); opcao=opcaolevar; } pthread_mutex_unlock( &chave ); } Programação de Sistemas Cafetaria : 7/14 Programa cliente (2) if ( opcao==opcaolocal ) { numblocal++; pthread_mutex_unlock( &chave ); sem_post( &filalocal ); /* sinaliza posto */ /* fica à espera do café */ pthread_mutex_lock( &entregalocal ); printf("[cliente %d]: recebe cafe' para tomar localmente\n",a->id); sleep( 6 ); printf( "[Cliente %d]: sai da loja\n",a->id ); } Programação de Sistemas Cafetaria : 8/14
Programa cliente (3) if ( opcao==opcaolevar ) { numblevar++; pthread_mutex_unlock( &chave ); sem_post( &filalevar ); /* sinaliza posto */ /* fica à espera do café */ pthread_mutex_lock( &entregalevar ); printf( "[Cliente %d]: recebe cafe' para levar\n",a->id ); printf( "[Cliente %d]: sai da loja\n",a->id ); } aservir--; if( aservir==0 ) { /* se for o ultimo cliente a sair, fecha os postos */ pthread_kill(levartid,sigusr1);pthread_kill(localtid,sigusr1);} pthread_mutex_unlock( &chave ); pthread_exit( NULL ); } Programação de Sistemas Cafetaria : 9/14 Programa programa principal (1) main(int argc,char *argv[]) { int i,clientes; pthread_t clientetid[customers]; argumentsp *argp; argumentsc *argc; if(argc!=2) { printf( "%s clientes\n",argv[0] ); _exit(2); } clientes = atoi(argv[1]); if( clientes>customers ) { printf( "Num maximo de clientes e' %d\n",customers ); _exit(2); } /* inicializa de semáforos e trancas */ sem_init( &filalevar,0,0 ); sem_init( &filalocal,0,0 ); pthread_mutex_init( &chave,null ); pthread_mutex_init( &entregalocal,null ); pthread_mutex_init( &entregalevar,null ); Programação de Sistemas Cafetaria : 10/14
Programa programa principal (2) numblocal = numblevar = 0; aservir = clientes; srand( time(null) ); /* lança os dois postos */ argp = (argumentsp *)malloc( sizeof(argumentsp) ); if( argp==null ) { printf( "Erro no malloc\n ); _exit(2); } argp->this = opcaolocal; argp->fila = &filalocal; argp->entrega = &entregalocal; if( pthread_create(&localtid,null,posto,(void *)argp) ) { printf( "Erro na criacao da caixa local\n ); _exit(2); } argp = (argumentsp *)malloc( sizeof(argumentsp) ); if( argp==null ) { printf( "Erro no malloc\n ); _exit(2); } argp->this = opcaolevar; argp->fila = &filalevar; argp->entrega = &entregalevar; if( pthread_create(&levartid,null,posto,(void *)argp) ) { printf( "Erro na criacao da caixa levar\n" ); _exit(2); } Programação de Sistemas Cafetaria : 11/14 Programa programa principal (3) /* lança clientes */ for ( i=0;i<clientes;i++ ) { argc = (argumentsc *)malloc( sizeof(argumentsc) ); if( argc==null ) { printf( "Erro no malloc\n" ); _exit(2); } argc->id=i; if( pthread_create(&clientetid[i],null,cliente,(void *)argc) ) { printf( "Erro na criacao do cliente %d\n",i ); _exit(2); } sleep( 2+(rand()%5) ); } /* aguarda finalização das threads */ for ( i=0;i<clientes;i++ ) pthread_join( clientetid[i],null ); pthread_join( localtid,null ); pthread_join( levartid,null ); Programação de Sistemas Cafetaria : 12/14
Programa programa principal (4) /* elimina semáforos e trancas */ if ( sem_destroy( &filalevar )==-1 ) { printf( "Erro na eliminacao do semaforo Levar\n" ); exit(4); } if ( sem_destroy( &filalocal )==-1 ) { printf( "Erro na eliminacao do semaforo Local\n" ); exit(4); } if(!pthread_mutex_destroy( &chave ) ) { printf( "Erro na eliminacao da tranca chave\n" ); exit(4); } if(!pthread_mutex_destroy( &entregalocal ) ) { printf( "Erro na eliminacao da tranca entregalocal\n" ); exit(4); } if(!pthread_mutex_destroy( &entregalevar ) ) { printf( "Erro na eliminacao da tranca entregalevar\n" ); exit(4); } /* aguarda finalização das threads */ _exit(0); } Programação de Sistemas Cafetaria : 13/14 Exemplo de execução [rgc@asterix cafe]$ cafe 5 [Cliente 0]: seleccionou indiferente-{local=0,levar=0}:colocado na caixa Levar Caixa levar prepara cafe [Cliente 1]: seleccionou levar [Cliente 2]: seleccionou indiferente-{local=0,levar=2}:colocado na caixa Local Caixa local prepara cafe Caixa levar entrega cafe Caixa levar prepara cafe [Cliente 0]: recebe cafe' para levar [Cliente 0]: sai da loja [Cliente 3]: seleccionou local [Cliente 3]: seleccionou local [Cliente 4]: seleccionou indiferente-{local=2,levar=1}:colocado na caixa Levar Caixa local entrega cafe Caixa local prepara cafe [Cliente 2]: recebe cafe' para tomar localmente Caixa levar entrega cafe Caixa levar prepara cafe [Cliente 1]: recebe cafe' para levar [Cliente 1]: sai da loja [Cliente 2]: sai da loja Caixa local entrega cafe [Cliente 3]: recebe cafe' para tomar localmente Caixa levar entrega cafe [Cliente 4]: recebe cafe' para levar [Cliente 4]: sai da loja [Cliente 3]: sai da loja [rgc@asterix cafe]$ Programação de Sistemas Cafetaria : 14/14