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
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;
}
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:
//var. globale in memoria condivisa
MT_BUFFER bancone = MT_BUFFER_new(n_camerieri);
ogni cliente che arriva inserisce il suo ordine nel bancone
codice:
struct ordinazione ord;
ord.primo = PASTA_E_FAGIOLI;
ord.secondo = MELANZANE_ALLA_PARMIGIANA;
ord.cliente = get_processID();
mt_push(bancone, &ord);
dopo di che aspetta di essere chiamato dal cassiere
codice:
msg = wait_for_message();
tra il cameriere e il cuoco ci sarà un'altra coda uguale identica
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
codice:
//var. globale in memoria condivisa
MT_BUFFER cucina = MT_BUFFER_new(n_cuochi * 10);
il loop del cameriere sarà una cosa di questo tipo
codice:
struct ordinazione * ord;
while (running)
{
ord = (struct ordinazione *)mt_pop(bancone);
mt_push(cucina, ord);
}
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:
//var. globale in memoria condivisa
MT_BUFFER casse = MT_BUFFER_new(n_cassieri * 2);
il loop del cuoco
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);
}
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...
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
codice:
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);
}
il cliente l'avevamo lasciato in attesa
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);
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..
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).