Scarica il documento per vederlo tutto.
Scarica il documento per vederlo tutto.
Scarica il documento per vederlo tutto.
Scarica il documento per vederlo tutto.
Scarica il documento per vederlo tutto.
Scarica il documento per vederlo tutto.
Scarica il documento per vederlo tutto.
Scarica il documento per vederlo tutto.
vuoi
o PayPal
tutte le volte che vuoi
Confronto tra tempi di creazione di thread e processi
Nella figura sotto possiamo osservare una tabella che mette in comparazione i tempi di creazione di un thread con i tempi di creazione di un processo. I tempi di creazione di un thread sono minori di un ordine di grandezza rispetto ai tempi di creazione di un processo. In tabella sono presenti dei processori al quanto vecchiotti. I guadagni che si ottengono con i thread non sono solo a livello prestazionale ma si hanno anche guadagni a livello di comunicazione. Per la comunicazione tra i processi bisogna inventarsi tutta una serie di meccanismi di comunicazione interprocesso, questi meccanismi IPC che sono shared memory, semafori e code di messaggi perché dopo la fork, dopo che ogni processo viene ad essere creato otteniamo la copia esatta del padre e quindi non abbiamo nessuna area condivisa tra i processi se non queste aree apposite create con funzionalità apposite del Kernel. Usando i thread la condivisione di dati è molto più efficiente e facile in quanto i thread.
hanno lo stesso spazio di indirizzamento mentre i processi no
I thread condividono lo stesso spazio di indirizzamento, mentre i processi hanno spazi di indirizzamento separati.
L'area heap viene condivisa tra i thread
L'area heap è condivisa tra i thread, mentre ogni thread ha le sue aree dati stack e codice specifiche.
Ogni thread ha il suo stack
Ogni thread ha il suo stack. Quando viene creato un thread utilizzando la funzione Pthread Create in Linux, vengono gestite le aree comuni e non comuni.
L'area dati globale è comune a tutti i thread figli di un thread padre
L'area dati globale è comune a tutti i thread figli di un thread padre.
facciamo applicazioni multiprocesso, quando si va ad implementare il main qui non viene ad essere automaticamente creato un processo all'atto dell'esecuzione del programma eseguibile ma viene ad essere creato un thread che prende il nome di thread master. In realtà in Linux/UNIX abbiamo che l'unità fondamentale è un task che altro non è che un thread, quindi abbiamo che è più facile la comunicazione in quanto non bisogna andare a gestire la creazione della shared memory, l'attach e tutte quelle cose che abbiamo visto essere dispendiose da utilizzare. In questo caso siccome le aree di memoria sono condivise, l'area heap e l'area dati globale, la libreria pthread fornisce dei meccanismi per la sincronizzazione che sono importanti per andare a disciplinare e per gestire una cooperazione o una competizione tra i thread che sono meccanismi necessari come quelli ad esempio usati per i processi. Si avranno quindi una sorta
disemafori che non sono semafori ma delle funzioni come quelle per la gestione dei thread, per la gestione dei mutex e per la gestione delle variabili condition. Implicitamente usando la libreria pthread sarà possibile andare ad utilizzare un mutex per gestire la sincronizzazione ma generalmente stiamo usando un monitor per gestire la cooperazione e la competizione tra i thread di uno stesso processo. Quando dobbiamo andare a gestire o implementare un'applicazione multi thread e dobbiamo andare a gestire un problema produttore consumatore questo lo andremo ad implementare come se fosse un problema produttore consumatore con monitor signal and continue e quindi il processo che segnala continuerà la sua esecuzione all'interno del monitor. N.B Oggi la libreria pthread e tutte le funzioni sono create all'interno di un branch della libreria C (Gnu LibC), se vogliamo possiamo andare ad osservare tutte le librerie disponibili alPuoi trovare il codice sorgente pthread su questo repository: [link](sitocode.woboq.org/userspace/glibc/npti/). La prima funzione che utilizzeremo per i pthread è `pthread_create`, che è l'equivalente di `fork`. Questa funzione richiede 4 parametri: - L'identificatore del thread creato di tipo `pthread_t`. - Gli attributi utilizzati per la creazione di questi thread, che permettono di impostare se i thread possono essere joinabili o meno (equivalente di `wait` per i processi). - Il parametro `start_routine`, che è un puntatore a `void` e rappresenta la funzione che deve essere invocata al momento della creazione. - Gli argomenti che vengono passati alla funzione. Possiamo notare che ci sono dei vincoli specificati nella firma di questa funzione.dobbiamo stare attenti agli ultimi due argomenti. Per quanto riguarda lostart_routine
dobbiamo dare attenzione che sia di tipo void *
e quindi questo ci obbliga ad utilizzare il valore di ritorno per la funzione che si va ad invocare che è del tipo void*
. Per passare dei parametri di tipo intero, di tipo struttura dobbiamo andare a fare il type casting inverso, andiamo quindi a passare un void *
e poi andremo a fare un type casting all'interno della funzione.
La funzione di terminazione abbiamo che rappresenta l'analogo della funzione exit
per un processo. Generalmente il thread abbiamo che può terminare naturalmente, quando invochiamo la pthread_exit()
esplicitamente, può essere cancellato da un altro thread che può far terminare prematuramente un altro thread tramite l'utilizzo della pthread_cancel()
oppure termina quando termina il processo.
La pthread_exit
oltre ad essere usata per far terminare esplicitamente un thread viene ad essereusata anche nel main alla fine e quindi quando terminiamo lo sviluppo dell'applicazione e questo lo si fa per evitare che altri thread continuino ad eseguire. Se si va a leggere il manuale viene ad essere riportato che è buona norma andare ad utilizzare la pthread_exit
in tutti i thread e poi una pthread_exit(NULL)
alla fine del main.
Nel caso in cui volessimo andare a passare più parametri alla start_routine
dobbiamo andare ad usare una struttura dati che contiene al suo interno gli N parametri, questa struttura dovrà essere contenuta nell'area heap e poi potrà essere passata attraverso un type casting a void
. Quando andiamo a creare il thread gli passiamo la start function
che ha un valore di ritorno di tipo void *
, il parametro è sempre un solo parametro di tipo void *
, questa non sarà una variabile semplice ma una variabile strutturata. Quando stiamo all'interno della struct function possiamo fare il type casting al tipo di dato.
pthread_create
ed usiamo il valore di ritorno per questa funzione che se è 0 indica che va tutto bene mentre se è diverso indica che c'è qualche problema. Possiamo andare a vedere la firma della pthread_create
usando il manuale man
. Possiamo osservare come questa funzione vada cercando un riferimento al thread e in questo caso è uno array di thread, possiamo osservare poi che abbiamo due void *
per la start routine e per la parte di argomenti che devono essere passati alla start routine. Nel nostro esempio abbiamo quindi bisogno del riferimento e due attributi che possiamo mettere a NULL, abbiamo poi che dobbiamo andare a passargli una funzione che è a start_routine che abbiamo detto il suo valore di ritorno è void *
, in questo caso dobbiamo semplicemente passare il nome della funzione chenonabbiamo ancora creato e dobbiamo poi passare il valore del parametro i che in questo caso dobbiamoandare a typecastere al void*.N.B La funzione print_hello non può essere una funzione del tipo perché il valore di ritorno deve essere obbligatoriamente void* ed è obbligatorio anche avere un unico parametro di ingresso alla funzione che è void *. Nel caso vogliamo passare più parametri andremo a passare sempre un riferimento ma un riferimento ad una struttura. Questa è una delle prime differenze rispetto ad utilizzare un'applicazione multiprocesso dove abbiamo una funzione classica mentre nel caso in esame la firma è obbligata ad essere come sopra.Quando realizziamo la funzione print_hello dobbiamo andare a mettere al suo interno una pthread_exit in quanto questa deve terminare. Nella pthread_exit possiamo andare a mettere come valore di uscita 0 o qualsiasi altro valore vogliamo in uscita. 746Quello che otteniamo con questo codiceè semplicemente che ogni volta che questo thread verrà ad esserechiamato andrà a stampare un hello per ogni thread creato fino ad un valore massimo di thread creati.
Se andassimo a compilare il programma sopra senza linkare la libreria thread otterremo quanto sotto.
Possiamo osservare che ci vengono ad essere dati solo dei warning e ci viene ad essere creato lo stessol’eseguibile ma se proviamo a mandare in esecuzione abbiamo che in automatico viene ad essere fattal’azione di linkaggio della quale ci eravamo dimenticati e questo è un side effect di MAC OS che ha capito inautomatico che stiamo usando la libreria pthread; se stiamo usando Linux dobbiamo invece ricordarci diandare a linkare la libreria pthread e quindi di andare ad usare il flag -l pthread.
Possiamo osservare poi che sopra si hanno dei warning particolari in quanto l’implementazione della libreria pthread è leggermentediversa da quella implementata in Linux.
747
allora che la riga di compilazione usata sopra non andrà bene a meno che non si è su un ambiente MAC; la riga di compilazione che dovrà essere usata sarà con il flag -lpthread quello che si va a dire al compilatore è che si intende usare ne