Hai presente il problema del produttore e del consumatore?
Fai conto di avere una coda del genere, in cui un produttore inserisce e un consumatore estrae
in pseudo codice
a questo punto la prima parte del problema, (i clienti che arrivano al bancone) dovranno inserire le loro richieste in un buffer di questo genere, il buffer dovrà avere almeno n posti, tanti quanti sono i camerieri.codice:#include <stdio.h> #include <stdlib.h> typedef struct _MT_BUFFER { semaphore_t push_sem; semaphore_t pop_sem; mutex_t exclusive; void ** buffer; int head; int tail; unsigned int bufsize; }*MT_BUFFER; MT_BUFFER MT_BUFFER_new(unsigned int size) { MT_BUFFER temp = shm_alloc(sizeof(struct _MT_BUFFER)); temp->buffer = (void**)shm_alloc(sizeof(void*)size); //controllare allocazione non sarebbe male sem_init(temp->push_sem, size); sem_init(temp->pop_sem, 0); mutex_init(temp->exclusive, UNLOCKED); bufsize = size; head=size-1; tail=size-1; return temp; } void MT_BUFFER_free(MT_BUFFER queue) { sem_terminate(queue->push_sem); sem_terminate(queue->pop_sem); mutex_terminate(queue->exclusive); shm_free(queue->buffer); shm_free(queue); } void mt_push(MT_BUFFER queue, void* data) { sem_down(queue->push_sem); mutex_lock(queue->exclusive); queue->buffer[tail] = data; --queue->tail; if(queue->tail<0) queue->tail = queue->bufsize - 1; mutex_unlock(queue->exclusive); sem_up(queue->push_sem); } void* mt_pop(MT_BUFFER queue) { void * retval; sem_down(queue->pop_sem); mutex_lock(queue->exclusive); retval = queue->buffer[queue->head]; head = (head+1) % queue->bufsize; mutex_unlock(queue->exclusive); sem_up(queue->push_sem); return retval; }
ogni cliente che arriva inserisce il suo ordine nel banconecodice://var. globale in memoria condivisa MT_BUFFER bancone = MT_BUFFER_new(n_camerieri);
dopo di che aspetta di essere chiamato dal cassierecodice:struct ordinazione ord; ord.primo = PASTA_E_FAGIOLI; ord.secondo = MELANZANE_ALLA_PARMIGIANA; ord.cliente = get_processID(); mt_push(bancone, &ord);
tra il cameriere e il cuoco ci sarà un'altra coda uguale identicacodice:msg = wait_for_message();
ma siccome sicuramente i cuochi saranno molto + lenti dei camerieri
questa coda sarà molto + lunga del numero di cuochi, altrimenti i camerieri saranno spesso bloccati in attesa dei cuochi
il loop del cameriere sarà una cosa di questo tipocodice://var. globale in memoria condivisa MT_BUFFER cucina = MT_BUFFER_new(n_cuochi * 10);
ora il principio si ripete anche tra cuochi e cassieri, il buffer lo lasciamo un po più grande per fare in modo che il cuoco non si debba sincronizzare troppo spesso con il cassiere.codice:struct ordinazione * ord; while (running) { ord = (struct ordinazione *)mt_pop(bancone); mt_push(cucina, ord); }
il loop del cuococodice://var. globale in memoria condivisa MT_BUFFER casse = MT_BUFFER_new(n_cassieri * 2);
invece il cassiere si comporta diversamente, legge dalla coda delle casse e urla a gran voce il numero del cliente che si sta servendo, il cliente si presenta, paga e se ne va...codice:struct ordinazione * ord; struct preparazione *prep;//anche questo punta alla mem condivisa while(running) { ord = (struct ordinazione *)mt_pop(cucina); prep = preparazione_new(); prep.primo = cucinailprimo(ord.primo); prep.secondo = cucinailsecondo(ord.secondo); prep.cliente = ord.cliente; mt_push(casse, prep); }
ecco il loop del cassiere, qua conviene usare qualche primitiva di scambio di messaggi per evitare di impantanarsi in decine di chiamate per la sincronizzazione
il cliente l'avevamo lasciato in attesacodice:struct preparazione *prep; // int soldi, resto; message_t msg; while(running) { prep = (struct preparazione *)mt_pop(casse); prep.conto = costo(prep.primo) + costo(prep.secondo); prep.cassiere = get_processID(); send_msg(prep.cliente, prep); msg = wait_for_message(); soldi = (int)msg.data; metti_in_cassa(soldi); resto = soldi - conto; send_msg(prep.cliente, resto); }
Questa è un idea, come implementarlo poi in C te lo lascio, perchè è un lavoro lungo..., nel main metterai tutti i fork necessari, accetterai l'imput e gestirai i segnali..codice:msg = wait_for_message(); prep = (struct preparazione*)msg.data; send_message(prep.cassiere, caccia_i_soldi(prep.conto)); msg = wait_for_message(); resto = (int)msg.data; mangia(prep.primo); mangia(prep.secondo); shm_free(prep); exit(0);
COmunque, riassumendo, l'uso della coda sincronizzata ti permette di scrivere un programma fair (se fosse uno stack forse qualche cliente potrebbe morire di fame), ti permette di evitare di sbatterti troppo a pensare come sincronizzare i vari personaggi: perchè ci pensa la coda, e usa relativamente poche risorse (2 semafori e un mutex, in tutto il programma sono 6 semafori e 3 mutex).

Rispondi quotando