Vai alla Home Page About me Courseware Federica Living Library Federica Federica Podstudio Virtual Campus 3D Le Miniguide all'orientamento Gli eBook di Federica La Corte in Rete
 
I corsi di Ingegneria
 
Il Corso Le lezioni del Corso La Cattedra
 
Materiali di approfondimento Risorse Web Il Podcast di questa lezione

Domenico Cotroneo » 26.Posix Threads


Introduzione

Un thread, o processo leggero, è un flusso di controllo sequenziale di un programma.

La coppia processo+threads Realizza la separazione tra concorrenza e protezione.

Nei sistemi Unix, un thread:

  • esiste all’interno di un processo e usa le sue risorse;
  • possiede il suo flusso di controllo indipendente (finché il processo padre esiste);
  • può condividere le risorse del processo con altri thread, che eseguono in maniera indipendente o dipendente;

Dal punto di vista di un programmatore, un thread può essere visto come una procedura che viene eseguita indipendentemente dal programma principale.

Processi e Thread

I processi contengono informazioni sulle risorse del programma e lo stato di esecuzione:

  • Process ID, process group ID, user ID, group ID;
  • Ambiente;
  • Directory corrente;
  • Registri (Program Counter, Stack Pointer, …);
  • Area codice del programma (text);
  • Area dati statici;
  • Area Stack;
  • Area Heap;
  • Descrittori dei file aperti;
  • Segnali;
  • IPC (code di messaggi, semafori, memoria condivisa).

Processi e Thread

I thread possono essere schedulati ed eseguiti indipendente perché possiedono i loro:

  • registri (Program Counter, Stack Pointer);
  • proprietà di scheduling;
  • segnali pendenti;
  • dati specifici.

I thread che eseguono in uno stesso processo condividono le risorse del processo, per cui:

  • le modifiche fatte da un thread sulle risorse condivise (p. es: un thread chiude un file) saranno viste da tutti gli altri thread;
  • è possibile leggere/scrivere la stessa locazione di memoria, per cui è richiesta una sincronizzazione esplicita tra i thread.

Processi e Thread


Tipici Modelli di programmazione multithread

Manager/Workers: un thread, il manager, riceve in input i comandi e assegna i lavori ad altri thread, i workers.

Pipeline: un task è suddiviso in una serie di operazioni più semplici, che possono essere eseguite in serie, ma concorrentemente, da diversi thread.

Peer: simile al modello Manager/Workers, ma una volta che il thread principale assegna il lavoro agli altri thread, partecipa attivamente anch’esso nel lavoro.

PThreads

Breve storia

  • Diverse librerie proprietarie per la gestione dei thread implementate da diversi produttori di hardware.
  • Forte eterogeneità tra le varie librerie.
  • Bisogno di un interfaccia standard.
  • Per i sistemi UNIX tale interfaccia fu specificata nel 1995 con lo standard IEEE POSIX 1003.1c (PThreads).

I PThreads sono stati definiti come un insieme di tipi e procedure implementate in C.
Sono composti da un file pthread.h e una libreria.

Perché PThreads?

Principalmente, per il guadagno in prestazioni.

Ma anche, la comunicazione inter-thread è molto più efficiente e semplice da usare della comunicazione inter-processo.

50000 creazioni di processi/thread; i tempi sono riportati in secondi

50000 creazioni di processi/thread; i tempi sono riportati in secondi


PThread APIs

Gestione dei Thread: creazione, distruzione e join di thread.

Gestione dei Mutex: creazione, distruzione, lock e unlock di variabili di mutua esclusione (mutex) per la gestione di sezioni critiche.

Gestione delle Condition Variables: creazione, distruzione, wait e signal su variabili condition definite dal programmatore.

Gestione dei ThreadCreazione di un Thread

pthread_create (thread,attr,start_r,arg)
Crea un nuovo thread e lo rende eseguibile.

  • thread(output): di tipo pthread_t, è un identificatore del thread creato;
  • attr(input): di tipo pthread_attr_t, serve a settare gli attributi del thread;
  • start_r (input): puntatore alla funzione C (starting routine) che verrà eseguita una volta che il thread è creato;
  • arg(input): argomento che può essere passato alla funzione C (ne va fatto il casting a void *).

Gestione dei Thread. Terminazione di un Thread

Un thread può terminare per diversi motivi:

  • la starting routine termina la sua esecuzione;
  • il thread chiama la pthread_exit();
  • il thread è cancellato da un altro thread con pthread_cancel();
  • l’intero processo termina.

pthread_exit (status)

  • Usata per terminare un thread esplicitamente.
  • Se usata nel programma principale (che potrebbe terminare prima di tutti i thread), gli altri thread continueranno ad eseguire.
  • E’ buona norma usarla in tutti i thread.
  • status (input): indica lo stato di uscita del thread.

Gestione dei Thread – Un esempio

Mostra codice

Gestione dei ThreadPassaggio di parametri

La pthread_create consente di passare un singolo argomento di tipo void *;
Per passare più di un argomento al thread, si può definire e passare una struct, facendone il casting a void *:

struct dati {

int dato1;

char dato2;

}

...

dati *d; d->dato1=10; d->dato2=‘c';

pthread_create(&id,NULL,start_f,(void *) d)

Nel thread sarà sufficiente effettuare il casting inverso:

dati* miei_dati = (struct dati *) d;

Gestione dei ThreadJoin

Il Join è un modo per sincronizzare più thread.

La chiamata pthread_join(threadId, status) blocca il chiamante finché il thread threadId specificato non termina;

La chiamata consente di ricavare lo stato di uscita del thread (status) specificato nella pthread_exit();


Gestione dei ThreadJoinable Threads

Un thread deve essere dichiarato come “joinable” affinché su di esso si possa effettuare l’operazione di join:

pthread_attr_t attr;

pthread_attr_init(&attr);

pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);

...

pthread_create(&id,&attr, start_r, (void *) data);

...

pthread_join(id, (void **) &status);

Gestione dei MutexCreazione e distruzione di Mutex

pthread_mutex_init (mutex,attr)

  • Crea un nuovo mutex e lo inizializza come “sbloccato” (unlocked).
  • mutex(output): di tipo pthread_mutex_t è un identificatore del mutex creato;
  • attr(input): per settare gli attributi del mutex;

pthread_mutex_destroy (mutex)

  • Distrugge un mutex che non serve più.

Gestione dei Mutex: lock e unlock

pthread_mutex_lock (mutex)

  • Un thread invoca la lock su un mutex per acquisire l’accesso in mutua esclusione alla sezione critica relativa al mutex. Se il mutex è già acquisito da un altro thread, il chiamante si blocca in attesa di un unlock.

pthread_mutex_trylock (mutex)

  • Analoga alla lock, ma non bloccante. Se il mutex è già acquisito, ritorna immediatamente con un codice di errore EBUSY.

pthread_mutex_unlock (mutex)

  • Un thread invoca la unlock su un mutex per rilasciare la sezione critica, e per consentire quindi l’accesso ad un altro thread precedentemente bloccato.

Gestione dei Mutex. Esercizio: prodotto scalare

Si vuole calcolare il prodotto scalare di due vettori di reali.

I vettori possono essere condivisi tra più thread.

Ogni thread calcola il prodotto scalare tra una parte dei due vettori, ed aggiorna una variabile condivisa contenente il risultato.


Condition Variables (CV)

Rappresentano ancora un altro mezzo di sincronizzazione, oltre che i Join e i mutex.

La sincronizzazione con CV si basa sui valori correnti dei dati (una condizione).

Vanno sempre usate in congiunzione con un mutex, realizzando un costrutto Monitor.

Gestione delle Condition Variables: Creazione e Distruzione

pthread_cond_init (condition,attr)

  • Crea una nuova CV e lo inizializza come “sbloccato” (unlocked).
  • condition(output): di tipo pthread_cond_t è un identificatore della CV creata;
  • attr(input): per settare gli attributi della CV.

pthread_cond_destroy (condition)

  • Distrugge una CV che non serve più.

Gestione delle Condition Variables: Wait e Signal

pthread_cond_wait (condition,mutex)

  • Blocca il thread chiamante finché non si invoca la signal sulla CV specificata.
  • La combinazione CV-mutex realizza di fatto un monitor:
    • enter_monitor → pthread_mutex_lock();
    • wait_cond → pthread_cond_wait(): rilascia automaticamente il mutex e blocca il chiamante;
    • quando viene ricevuta la singal, e il thread si risveglia, il mutex viene di nuovo bloccato, automaticamente;
    • leave_monitor → Il programmatore è responsabile di sbloccare il mutex con una pthread_mutex_unlock().

pthread_cond_signal (condition)

  • Serve a risvegliare un thread precedentemente bloccato sulla CV.
  • Di seguito alla signal, si dovrebbe sbloccare il mutex affinchè la wait completi la sua esecuzione.

I materiali di supporto della lezione

Dispensa “Programmazione dei POSIX Thread”

  • Contenuti protetti da Creative Commons
  • Feed RSS
  • Condividi su FriendFeed
  • Condividi su Facebook
  • Segnala su Twitter
  • Condividi su LinkedIn
Progetto "Campus Virtuale" dell'Università degli Studi di Napoli Federico II, realizzato con il cofinanziamento dell'Unione europea. Asse V - Società dell'informazione - Obiettivo Operativo 5.1 e-Government ed e-Inclusion