Concorrenza e rientranza

di il
8 risposte

Concorrenza e rientranza

Buongiorno,
di prima mattina ho fatto un ragionamento, e vorrei sapere da voi esperti dove sbaglio (se sbaglio).
Se costruisco una funzione che usa variabili globali (non rientrante), e il codice di questa funzione lo racchiudo fra l'acquisizione e il rilascio di un semaforo, rendo la funzione thread-safe, in quanto costruirò le altre funzioni che operano sulla stessa variabile globale (che saranno chiamate in altri thread) con lo stesso principio, cioè per essere eseguite dovranno acquisire lo stesso semaforo della funzione di prima. Però, se non faccio in questo modo (cioè in una di queste funzioni che operano su quella variabile globale non uso i meccanismi di lock), posso modificare la variabile globale nonostante abbia reso thread-safe quelle funzioni, dato che quest'ultima funzione senza meccanismi di lock può intervenire in qualsiasi momento. Ora, vale lo stesso con le funzioni thread-safe di libreria? L'essere thread-safe mette al riparo da tutto oppure, per i motivi di cui sopra, ci sono comunque i rischi legati alla non rientranza di queste funzioni? Oppure le funzioni non rientranti di libreria operano su variabili non locali sulle quali però non operano funzioni diverse?
Un'altra cosa: per quanto riguarda la non rientranza nei signal handler (non in concorrenza quindi), secondo me non è risolvibile coi semafori, dato che un segnale può interrmpere in qualunque momento: l'unica soluzione è usare funzioni rientranti nei signal handler. O no?
Grazie

8 Risposte

  • Re: Concorrenza e rientranza

    Nessuno che ne capisca di programmazione concorrente?
  • Re: Concorrenza e rientranza

    Riassumiamo:
    Se costruisco una funzione che usa variabili globali (non rientrante), e il codice di questa funzione lo racchiudo fra l'acquisizione e il rilascio di un semaforo, rendo la funzione thread-safe, in quanto costruirò le altre funzioni che operano sulla stessa variabile globale (che saranno chiamate in altri thread) con lo stesso principio, cioè per essere eseguite dovranno acquisire lo stesso semaforo della funzione di prima. Però, se non faccio in questo modo (cioè in una di queste funzioni che operano su quella variabile globale non uso i meccanismi di lock), posso modificare la variabile globale nonostante abbia reso thread-safe quelle funzioni, dato che quest'ultima funzione senza meccanismi di lock può intervenire in qualsiasi momento.
    Perfetto!
    Ora, vale lo stesso con le funzioni thread-safe di libreria? L'essere thread-safe mette al riparo da tutto oppure, per i motivi di cui sopra, ci sono comunque i rischi legati alla non rientranza di queste funzioni?
    L'essere thread safe mette dal riparo da tutto.

    Ma attenzione: questo finche' non vai a pasticciare con le strutture dati interne utilizzate da queste funzioni. Ma questo non puo' essere fatto, se ti attieni all'uso normale della libreria.

    Lo puoi fare usando sistemi trucchettosi: reflection (java, C#), oppure mediante l'analisi del codice binario. Ma se sai fare queste cose, conosci anche le problematiche del multithreading

    Se devi implementare delle funzioni thread-safe, quello che fai e' fare in modo che nessuno a parte le tue funzioni, possa accedere alle strutture dati di servizio, proprio per avere un controllo totale, e quindi coordinarne l'accesso, a tali informazioni.
    Un'altra cosa: per quanto riguarda la non rientranza nei signal handler (non in concorrenza quindi), secondo me non è risolvibile coi semafori, dato che un segnale può interrmpere in qualunque momento: l'unica soluzione è usare funzioni rientranti nei signal handler. O no?
    Non e' vero: il semaforo assicura che il signal handler venga eseguito da un thread alla volta.

    La situazione limite e' quando all'interno dell'implementazione del signal handler, viene richiamato lo stesso signal handler!
    Ma compito del programmatore e' assicurarsi che questo non possa mai succedere (generalmente mediante un'opportuna implementazione, oppure mediante dei flag).

    Una funzione e' rientrante se l'essere eseguita da piu' thread contemporanemante non genera comportamenti strani. Quindi usa solo variabili locali, allocate sullo stack del thread corrente, o nello heap al momento della chiamata, ed eventualmente chiama altre funzioni rientranti o thread-safe

    Una funzione rientrante e' ovviamente/naturalmente thread-safe

    Non tutte le funzioni sono thread-safe, pero'.

    Perche' non si implementano tutte le funzioni in modo da essere thread-safe?

    Il problema e' l'efficienza: assicurare che una funzione sia thread-safe puo' ridurre le performance della funziona anche di un fattore 1000. E nel 99% dei casi non serve, nel senso che chi progetta il softwrae puo' identificare i soli punti in cui serve effettivamente una sincronizzazione.

    Ed in un programma anche complesso, questi punti sono generalmente molto pochi: quindi e' sufficiente che il programmatore si assicuri il controllo della concorrenza solo in questi punti.

    Alcune funzioni devono necessariamente essere thread safe, comunque: come minimo

    1) l'allocazione della memoria
    2) la creazione dei thread
    3) l'accesso ad altre risorse gestite direttamente dal sistema operativo (file, tastiera, monitor, ...)

    questo perche' il loro controllo e' al di fuori delle possibilita' del programma in cui vengono eseguite, oltre la fatto che dimenticarsene sarebbe disastroso. Quindi poiche' deve essere sempre fatto, tanto vale che lo facciano direttamente loro.
  • Re: Concorrenza e rientranza

    Grazie mille per la precisa spiegazione! Per quanto riguarda la prima parte, mi hai tolto un grosso dubbio. Se hai pazienza, però, potresti chiarire qualche concetto riguardo i signal handler? Perché credo ci sia stato un fraintendimento: quando parlavo di signal handler intendevo specificare un altro concetto, infatti avevo scritto "non in concorrenza". Spiego con un esempio, forse è più facile: nel main faccio:

    - acquisizione semaforo
    - chiamata funzione non rientrante fnr()
    - rilascio semaforo

    Ora supponiamo che nel signal handler ad un certo punto ci sia la chiamata alla stessa fnr() anche questa racchiusa fra l'acquisizione e il rilascio dello stesso semaforo del main

    Se quando sono nel main e specificatamente quando sta eseguendo fnr() arriva il segnale viene chiamato il signal handler, ah!!! Capito! quando l'esecuzione del signal handler arriva al semaforo si blocca, riparte il main, viene eseguita fnr(), viene rilasciato il semaforo e riparte il signal handler che esegue fnr(). L'ho capito mentre scrivevo la domanda! L'ho capito oppure ho sbagliato qualcosa? Sempre mentre scrivo mi viene il dubbio: riparte il signal handler o continua l'esecuzione del main? Non lo so, mi sto perdendo...
  • Re: Concorrenza e rientranza

    Ora supponiamo che nel signal handler ad un certo punto ci sia la chiamata alla stessa fnr() anche questa racchiusa fra l'acquisizione e il rilascio dello stesso semaforo del main
    Generalmente lo stesso thread puo' acquisire un lock che ha gia' quante volte vuole, e lo deve rilasciare ovviamente lo stesso numero di volte. Se non lo puo' fare, di sicuro c'e' un flag o un tipo diverso di semaforo che lo permette.

    Ora mentre un thread ha acquisito il semaforo, qualunque altro thread che tenta di acquisire lo stesso semaforo, si mette in attesa.

    Quando il thread corrente rilascia per l'ultima volta il semaforo, uno dei thread in attesa parte e lo acquisisce a sua volta, mentre i rimamenti continuano ad aspettare.

    E' cosi' via.

    Certo che ti sei cercato l'esempio piu' incasinato!

    Thread-safe e pure ricorsivo

    Vabbe' dai, non e' poi difficile. Basta farci la mano
  • Re: Concorrenza e rientranza

    Grazie, purtroppo adesso non ho tempo per rifletterci su. Quando avrò un po' più di tempo penserò a quello che hai scritto.
  • Re: Concorrenza e rientranza

    Quello che dici mi è chiaro, ma forse non mi ero spiegato bene riguardo quale fosse il mio dubbio. Prima di esporlo, una domanda su quello che hai detto: tu dici che le funzioni per accedere alle risorse di sistema son thread safe, però io so che la printf() non lo è.
    Adesso cerco di spiegare cosa non mi torna. Allora, siamo in una situazione di NON concorrenza, cioè c'è un solo thread che viene eseguito (il main). Questo thread può essere interrotto da un segnale, e quindi faccio un gestore di segnali (che è una funzione particolare, ma non un thread). Supponiamo che all'interno del main ci sia una printf() e anche all'interno del signal handler ci sia una printf(). Ora, il main viene interrotto dal segnale proprio mentre sta eseguendo la printf(), cioè l'esecuzione della printf() viene interrotta. C'è un problema, perché anche il signal handler deve eseguire la printf(), e questa non è rientrante. Allora io metto i semafori prima e dopo (acquisizione e rilascio) le printf() sia nel main sia nel gestore di segnali. Quindi in questo modo quando l'esecuzione del signal handler arriverà al semaforo che precede la printf(), tale esecuzione si bloccherà, perché il semaforo è occupato, e quindi riprenderà ad essere eseguito il main che completerà la printf() e rilascerà il semaforo. Adesso viene il dilemma: rilasciato il semaforo, lo scheduler farà ripartire il signal handler o continuerà ad essere eseguito il main? A rigor di logica, dovrebbe ripartire l'esecuzione del signal handler, affinché tutto funzioni, quindi presumo che lo scheduler, quando arriva il segnale, dia al signal handler una priorità maggiore rispetto al main; in pratica, la conclusione a cui sono giunto, è che lo scheduler tratti il gestore di segnali proprio come un thread, che viene creato quando arriva il segnale e gli viene data la priorità più alta. Questo secondo la logica (la mia logica, infatti sto domandando se sia giusta).
    Grazie
  • Re: Concorrenza e rientranza

    Lo scheduler agisce solo sui thread.

    Qualunque funzione non puo' essere eseguita se non all'interno di un thread.

    Un signal handler puo' essere attivato dal thread stesso, ad esempio a fronte di un'eccezione, oppure da un'altro thread, ad esempio il ctrl-c, che viene eseguito dal thread di gestione degli eventi di sistema.

    Quindi, la tua logica e' giusta, a parte l'idea che lo scheduler faccia partire il signal handler: lo scheduler fa partire il thread che sta eseguendo il signal handler. E tale thread non lo genera lo scheduler, ma chi genera il segnale, a cui il signal handler e' associato.
  • Re: Concorrenza e rientranza

    migliorabile ha scritto:


    Lo scheduler agisce solo sui thread.

    Qualunque funzione non puo' essere eseguita se non all'interno di un thread.

    Un signal handler puo' essere attivato dal thread stesso, ad esempio a fronte di un'eccezione, oppure da un'altro thread, ad esempio il ctrl-c, che viene eseguito dal thread di gestione degli eventi di sistema.

    Quindi, la tua logica e' giusta, a parte l'idea che lo scheduler faccia partire il signal handler: lo scheduler fa partire il thread che sta eseguendo il signal handler. E tale thread non lo genera lo scheduler, ma chi genera il segnale, a cui il signal handler e' associato.
    Capito. Deduco che il thread che esegue il signal handler abbia priorità più alta, per i motivi di cui al post precedente.
Devi accedere o registrarti per scrivere nel forum
8 risposte