I principali vantaggi dei thread è quello che nei processori moderni sono capaci di elaborare 2 thread contemporaneamente, rispetto ad un singolo processo, questo perché posseggono il doppio dei registri.
Da notare. È importante all'interno della funzione che verrà eseguita dal main non dichiarare variabili con lo stesso nome di quelle globali. Altrimenti verranno prese in considerazione solo quelle globali.
Esempio:
int a=2;
int
main() {...}
Codice
thread:
{
int a=15;
printf(“%i\n”,a);
}
In
questo caso, sempre che stampi, verrà stampato il valore 2 in quanto
a è una variabile globale
- Vengono gestiti direttamente dal processo padre, simulando la concorrenza (cioè in user space) :
Non dovendo eseguire chiamate di sistema per la gestione dei thread, essa risulterà molto più rapida. Il padre dovrà gestire la sincronizzazione dei vari thread e non la monopolizzazione da parte di un thread della CPU
- Creando i thread in kernel space
Creando i thread in kernel space i thread saranno considerati come dei processi, perciò sarà lo stesso S.O. (o più precisamente lo scheduler) a gestire i thread. Purtroppo questo metodo è più lento rispetto alla sua controparte in user space perché ci saranno più chiamate di sistema per la gestione dei thread.
- Creo i thread in user mode, ma li faccio gestire dal S.O.
L'ideologia di gestione di thread che andremo a considerare.
Il padre ( si intende il thread master, cioè il main), per poter gestire la concorrenza tra i vari thread deve utilizzare dei protocolli: un protocolli sono un insieme di regole che devono essere soddisfatte per affinché la comunicazione, o in questo caso la concorrenza tra thread sia gestita in maniera corretta.
Il S.O. invece entrerà in gioco con le variabili Mutex: queste variabili sono in kernel space e per modificarle bisogna utilizzare delle apposite chiamate di sistema. Queste variabili, dette anche variabili di lock, possono essere utilizzate come semafori per l'accedere alle zone critiche del programma.
Condizione critica: Lettura e scrittura di una variabile globale contemporaneamente tra 2 o più thread
Regione critica: zone di istruzioni che devono essere eseguite da un unico processo dall'inizio alla fine, uno alla volta
Le quattro regole per una buona programmazione concorrente:
- Non ci sia più di un processo nella stessa zona critica
- Il programma deve funzionare indipendentemente dal numero di processori e dalla loro velocità
- I processi che non stanno eseguendo operazioni critiche non devono poter bloccare nessun processo.
- Nessun processo deve avere un'attesa infinita per entrare in una regione critica
(Queste regole valgono anche per i thread)
Il code segment contiene le istruzioni da eseguire
Nel data segment sono contenute le variabili globali (dette anche statiche, perché dichiarate all'inizio del programma) e dalle variabili mallocate, cioè quelle dichiarate dinamicamente.
Nello stack sono contenute tutte le variabili del proprio ambiente più altre informazioni indispensabili come lo stato del processore, il process counter ecc...
Queste informazioni sono indispensabili per poter cambiare l'esecuzione da un processo all'altro: Se il processo, dopo esser stato messo nello stato di ready torna ad essere eseguito deve sapere a che istruzione è arrivato, che stato aveva il processore a quell'istruzione, il valori delle variabili locali ecc... Quindi senza queste informazioni il processo non può continuare.
Per poter creare i thread bisogna includere una libreria che si chiama pthread.h ( #include <pthread.h>). Quando si compilerà con il gcc bisognerà implementare il parametro -pthread
Il comando per creare un thread è il ptread_create, che avrà bisogno di 4 parametri per poter essere eseguita.
Il primo parametro è il puntatore di una variabile pthread_t : in questa variabile sarà contenuto il valore del tid ( thread identifier ; è come il pid per i processi), il secondo parametro è utilizzato per creare un thread con determinati attributi, passandogli il parametro NULL creeremo un thread con gli attributi di default, il terzo parametro dovrà avere il nome di una funzione void*. Questa funzione dovrà avere un unico parametro che dovrà essere anch'esso un puntatore void. Il quarto parametro sarà il puntatore della funzione che abbiamo passato come 4 parametro.
La pthread_create fa ritornare un valore
Da notare. E qui sorge un problema: se dobbiamo passare più variabili, ma abbiamo solo un puntatore da poter passare al thread, come possiamo fare? Per risolvere questo problema si può utilizzare una struttura (una struct) contenente tutti i parametri che ci servono passare al thread, passandogli solamente il puntatore della struct.
La funzione che fa aspettare il thread master la fine dei thread figli è il comando pthread_join. Questo comando, molto simile alla waitpid. Infatti, ci sono 2 parametri da passare: uno sarà una variabile pthread_t che conterrà il tid del thread, il secondo parametro è un puntatore, che noi gli passeremo NULL).
(Guardare anche sul manuale la spiegazione di pthread_create e _join, scrivendo da terminale man phtread_create e pthread_join)
Ringraziate Tasca che mi ha aiutato per completare gli appuntolli
Nessun commento:
Posta un commento