Visualizzazione dei risultati da 1 a 4 su 4
  1. #1

    [C] progetto su programmazione concorrente e semafori

    Salve capoccioni... devo fare un progettino per l'uni...volevo qualche consiglio.. il problema riguarda la programmazione concorrente e la sincronizzazione tra i processi... in modo astratto si può esporre così:

    c'è un fastfood in cui arriva un numero non determinabile di clienti.. al bancone a servirli ci sta un certo numero di camerieri [parametro passato in input] che raccolgono le ordinazioni dei clienti e le passano ai cuochi... i cuochi sono acnh'essi in numero variabile [sempre da input] e devono preparare un piatto [sempre lo stesso per semplicità] e passarlo ai cassieri [anche questi in numero variabile] che provvedono a consegnare ilpiatto al cliente e incassare.

    Uscendo dall'astrazione quello che interessa al mio porf è effettuare la comunicazione tra i vari "personaggi" che popolano questa scena attraverso memoria condivisa [anche se poi il dato, l'ordinazione, non è determiinante in quanto l piatto è sempre lo stesso] e gestire le varie code che si possono creare per via del numero variabile dei personaggi....

    qualcuno ha qualche idea?

    io pensavo ad un main che non faccia altro che generare i clienti [in distribuzione geometrica.. ma questa è fesseria ] generando opportuni processi con fork() ed exec() [altra tecnica vista all'uni quindi presumo sia quella richiesta dal prof]... poi però non so come gestire bene il resto... sicuramente ci vuole un semaforo di accesso al bancone inizializzato in base al numero di camerieri.. ma non so bene come i camerieri [e gli altri personaggi] vadano inizializzati

    si accettano suggerimenti

  2. #2
    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).

  3. #3
    ah, all'occhio che non dovrebbe mai succedere che un processo legga e scriva nella coda, altrimenti rischi il deadlock.

  4. #4
    ti ringrazio, sei stato gentilissimo... vedo di leggere meglio più tardi che ora sono a letto influenzato... mi serviva solo una linea guida... credo che quanto hai scritto sia poi più che sufficiente
    Certo che il codice poi lo scrivo io... altrimenti lo fai tu il progetto

    grazie per ora, temo che abuserò ancora della tua gentilezza

Permessi di invio

  • Non puoi inserire discussioni
  • Non puoi inserire repliche
  • Non puoi inserire allegati
  • Non puoi modificare i tuoi messaggi
  •  
Powered by vBulletin® Version 4.2.1
Copyright © 2024 vBulletin Solutions, Inc. All rights reserved.