Vous êtes sur la page 1sur 32

Ecole Nationale Polytechnique d’Oran- Maurice AUDIN

Département de Génie des Systèmes


Intitulé de la matière: Programmation Système Avancée

Les threads (Processus légers)

Mme Nawel BENDIMERAD

Année universitaire 2022/2023


1
Objectifs du cours
1. Assimiler le concept de thread (processus léger)
2. Distinguer les différents modèles d’interaction entre threads
3. Créer des threads POSIX sous Linux

2
Contenu

1. Présentation des threads


2. Modèles d’interaction entre threads
3. Création des threads sous linux
4. Les attributs d’un thread
5. Relâchement du processeur par un thread
6. Implémentation du multithreading

3
1. Présentation des threads (1)
1.1 Définition
• Les threads permettent de simplifier la structure d’un programme.
• Un thread peut être définie comme un fil d’exécution, une tâche
(activité) ou processus léger (par opposition au processus lourd
créé par fork())
• Les threads permettent de dérouler plusieurs suites d’instructions,
en parallèle, en exécutant une fonction à l’intérieur du même
processus.
• Un thread peut être considéré comme une forme de mini-
processus. Plusieurs mini-processus peuvent s’exécuter en
parallèle dans un même programme.
• Un processus comporte un ou plusieurs threads.
• Un thread appartient toujours au même processus.
4
1. Présentation des threads (2)
1.2 Différence entre processus et thread
• Tout processus a un thread principal, à partir duquel d’autres
threads peuvent être lancés.
• Les threads d’un même processus partagent le même espace
d’adressage.
• Toutes les ressources du processus peuvent être accédées par
tous les threads, ce qui n’est pas le cas entre deux processus
distincts. Toutefois, chaque thread possède son propre compteur
programme (PC), un ensemble de registres, et une pile.
• Pour faire communiquer un processus père avec un processus
fils, nous devons utiliser des mécanismes de communication
interprocessus tandis que les threads d’un même processus
peuvent communiquer en lisant et en modifiant les variables de
leur processus.
5
1. Présentation des threads (3)
1.3 Avantages (1)
Les avantages du multi-threading par opposition au multi-processing
peuvent être résumés comme suit:
• Eviter l’exécution séquentielle des instructions et donc possibilité
d’accomplir plusieurs tâches à la fois.
 Ce qui permet d’améliorer le débit de traitement des données
dans une application.
• Dans le cas des systèmes multi-processeurs, les threads peuvent
s’exécuter complètement en parallèle sur chacun des processeur.
 Ceci permet d’exploiter au mieux ce type de systèmes.
• En comparaison avec un système multi-processus, une application
multi-threads requiert une surcharge (overhead) moindre pour la
gestion des tâches.

6
1. Présentation des threads (4)
1.3 Avantages (2)
• Le changement de contexte entre deux threads est beaucoup
moins gourmand en ressources et plus rapide par rapport à un
changement de contexte entre deux processus.
• Un programme décomposé en threads permet de mieux gérer les
entrées/sorties et le calcul.
 Un thread peut s’occuper du calcul, tandis que d’autres gèrent
les entrées/sorties.
• Comme les threads d’un même processus partagent le même
espace d’adressage donc ils peuvent communiquer entre eux sans
faire appel au kernel.
 Gain de temps par rapport à la communication entre processus.

7
1. Présentation des threads (5)
1.4 Inconvénients

• Etant donné que les threads d’un même processus partagent le


même espace d’adressage, un thread peut facilement corrompre
les données utilisées par un autre thread.
 Requiert des mécanismes de synchronisation lors d’accès
mémoires concurrents.
• Toujours lié au partage de l’espace d’adressage, si un thread
effectue un accès mémoire erroné fatal, le processus entier risque
de se terminer. Ce qui n’est pas le cas pour un système à plusieurs
processus.
• Un thread est lié à un programme particulier, et ne peut donc pas
être lancé par un autre programme. Un processus peut en
revanche être lancé par un autre processus, et donc être plus
aisément réutilisé. 8
2. Modèles d’interaction entre threads (1)
• Chaque programme comportant des processus légers est différent.
• Cependant, certains modèles communs existent. Ces modèles
permettent de définir comment une application attribue une
activité à chaque processus léger et comment ces processus légers
communiquent entre eux.
• Nous pouvons distinguer trois types de modèles, qui définissent
la façon dont les threads interagissent entre eux :
 Le modèle délégation (répartiteur/travailleurs ou
maître/esclaves)
 Le modèle pair (modèle en groupe)
 Le modèle pipeline

9
2. Modèles d’interaction entre threads (2)
2.1 Le modèle délégation (1)
• Dans le modèle délégation, un thread principal s’occupe de
répartir la charge de travail sur les threads travailleurs.
• Le thread principal appelé le répartiteur ou le maître reçoit des
requêtes pour tout le programme. En fonction de la requête
reçue, le répartiteur attribue l'activité à un ou plusieurs
processus légers travailleurs (ou esclaves).
• Exemple: serveur FTP, serveurs de bases de données, etc. où
un thread attend des commandes et les délègue ensuite aux
autres threads.

10
2. Modèles d’interaction entre threads (3)
2.1 Le modèle délégation (2)
Processus

Tâche 1

Thread Principal
Requêtes Tâche 2
(Répartiteur)

Tâche 3
.
.
.
Remarque: Pour la réalisation de ce modèle, nous pouvons par exemple utiliser
une boucle dans laquelle le thread principal attend des requêtes à exécuter et crée
ensuite un thread capable de répondre à chaque requête.
11
2. Modèles d’interaction entre threads (4)
2.2 Le modèle pair (1)
• Dans le modèle pair, aucun thread n’est principal, tous étant égaux
au niveau hiérarchique.
• Chaque processus léger réalise des traitements indépendamment,
sans être piloté par un répartiteur (les traitements à effectuer sont
déjà connus).
• Chaque processus léger traite ses propres requêtes (mais il peut être
spécialisé pour un certain type de travail).
• Le modèle en pair convient aux applications ayant des traitements
définis à réaliser.
• Exemple : une simulation d’un système physique décomposé en
éléments finis, comme la modélisation de la chaleur dans une pièce,
où on pourrait répartir le calcul entre plusieurs threads, chacun étant
responsable d’une partie de la pièce.
12
2. Modèles d’interaction entre threads (5)
2.2 Le modèle pair (2)

Processus

Tâche 1

Requêtes Tâche 2
Remarque: La synchronisation
entre threads risque fort d’y être
Tâche 3 nécessaire, afin que la tâche
globale s’exécute correctement.
.
.
.
13
2. Modèles d’interaction entre threads (6)
2.3 Le modèle pipeline (1)
• L'exécution d'une requête est réalisée par plusieurs processus
légers exécutant une partie de la requête en série.
• Les traitements sont effectués par étapes du premier processus
léger au dernier.
• Un étage du pipeline correspond à un traitement particulier à
appliquer aux données. Le thread responsable de cet étage attend
des données de l’étage précédent, traite ces données, et transmet
ensuite le résultat de son traitement au thread responsable de
l’étage suivant.
• Exemple: le traitement sur un flux vidéo, où un étage modifierait
la couleur, un autre le son, un autre appliquerait de la
compression, etc.

14
2. Modèles d’interaction entre threads (7)
2.3 Le modèle pipeline (2)

Processus

Requêtes Tâche 1 Tâche 2 Tâche 3 Résultat

Remarque: Le modèle pipeline est exploitable lorsque


l’application traite une longue chaîne d’entrée.

15
3. Création des threads sous Linux (1)
• La création d’un thread avec les attributs attr, en exécutant la
fonction fonc avec arg comme paramètre est réalisée comme suit:
int pthread_create(pthread_t *tid, pthread_attr_t *attr,
void *fonc, void *arg);
• attr : si NULL, le thread est créé avec les attributs par défaut.
• La valeur de retour :
 0 en cas de succès.
 En cas d’erreur une valeur non nulle indiquant l'erreur:
- EAGAIN : manque de ressource.
- EPERM : n’a pas de permission pour le type d'ordonnancement

demandé.
- EINVAL : les attributs spécifiés par attr ne sont pas valables. 16
3. Création des threads sous Linux (2)
Terminaison et suspension d’un thread
int pthread_exit((void*)status)
• Permet au thread appelant de terminer son exécution, en
fournissant son code de retour dans status.

int pthread_join(th, (void*)&status)


• Cet appel système suspend l’exécution du thread créateur jusqu’à
ce que termine le processus léger avec l’identificateur th.
• Si la valeur de status est non nulle, le valeur passée à
pthread_exit() par le thread terminé est mémorisée à cette
adresse.

17
4. Les attributs d’un thread (1)

• Les attributs du thread proposent un mécanisme pour


contrôler finement le comportement de threads individuels.
• Les attributs sont fixés lors de la création du thread.
• La fonction pthread_create accepte un argument qui est un
pointeur vers un objet d’attributs de thread.
• Si nous passons un pointeur nul, les attributs par défaut seront
utilisés pour configurer le nouveau thread.
• Cependant, nous pouvons créer et personnaliser un objet
d’attributs de threads pour spécifier nos propres valeurs.
• Chaque attribut possède une valeur par défaut.

18
4. Les attributs d’un thread (2)
Pour indiquer des attributs de thread personnalisés, il faut suivre les
étapes suivantes :
• Créer un objet pthread_attr_t. La façon la plus simple de le faire
est de déclarer une variable de ce type. Les attributs des threads
sont regroupés dans cet objet.
• Appeler pthread_attr_init en lui passant un pointeur vers cet objet.
Cela initialise les attributs à leur valeur par défaut.
• Modifier l’objet d’attributs pour qu’ils contiennent les valeurs
désirées.
• Passer un pointeur vers l’objet créé lors de l’appel pthread_create.
• Appeler pthread_attr_destroy pour libérer l’objet d’attributs.
19
4. Les attributs d’un thread (3)

• Chaque attribut possède un nom utilisé pour construire les


noms de deux types de fonctions :
• Fonction utilisée pour extraire la valeur de l’attribut nom de la
variable attr:
pthread_attr_getnom (pthread_attr_t *attr, …)
• Fonction utilisée pour modifier la valeur de l’attribut nom de
la variable attr:
pthread_attr_setnom (pthread_attr_t *attr, …)

20
4. Les attributs d’un thread (4)
Exemples de fonctions
• Déclaration et initialisation des attributs:
pthread_attr_t attr;
pthread_attr_init(&attr);
• Obtenir/modifier la taille de la pile d’un thread:
size_t taille;
pthread_attr_getstacksize(&attr, &taille);
pthread_attr_setstacksize (&attr, taille);
• Modifier la politique d’ordonnancement:
pthread_attr_setschedpolicy(&attr,SCHED_RR);
• Création du thread avec les attributs personnalisés:
pthread_create (&tid, &attr, fct, NULL);
phread_attr_destroy(&attr);
21
4. Les attributs d’un thread (5)
Détachement d’un thread
• Un thread peut être un thread joignable (par défaut) ou
un thread détaché.
• Les ressources allouées à un thread ne sont libérées qu’à la fin
du thread et que lorsque pthread_join a été effectué.
• Si l’on ne souhaite pas se synchroniser sur la fin d’un thread et
qu’on ne tient pas compte de son code de retour, donc on peut
le détacher.
• Dans ce cas, les ressources seront libérées à la fin de l’activité
du thread en exécutant pthread_exit.
• Attribut utilisé pour le détachement d’un thread:
PTHREAD_CREATE_DETACHED

22
4. Les attributs d’un thread (6)
Détachement d’un thread
Il existe deux méthodes pour le détachement d’un thread:
• Fonction pthread_detach :
int pthread_detach(pthread_t tid);
• Lors de sa création :
Exemple:
pthread_attr_t attr;
pthread_attr_init(&attr);
pthread_attr_setdetachstate(&attr,
PTHREAD_CREATE_DETACHED);
pthread_create (&tid, &attr, fct, NULL);

23
5. Relâchement du processeur par un
thread
• La demande de relâchement du processeur par un thread est
réalisée par la fonction suivante :
int sched_yield (void);
• Le thread appelant demande à libérer volontairement le processeur
avant que ne se termine son quota de temps ou avant d’être bloqué.
• Ce thread sera placé dans la file des "threads prêts".
• Il reprendra son exécution lorsque tous les threads de priorité
supérieure ou égale à la sienne auront terminé leur exécution.
• Cet appel système est utilisé par les threads car ces derniers
coopèrent alors que les processus sont généralement concurrents
pour l’accès au processeur.

24
6. Implémentation du multithreading (1)
• L’implémentation du multithreading varie d’une plateforme à
une autre (threads du SE Windows, threads du SE Linux,
threads POSIX, etc)
• Le multithreading peut être implémenté à différents niveaux:
 au niveau utilisateur (threads utilisateur) -> modèle plusieurs-
à-un,
 au niveau noyau (threads noyau) -> modèle un-à-un, ou
 aux deux niveaux (threads hybrides) -> modèle plusieurs-à-
plusieurs.

25
6. Implémentation du multithreading (2)
6.1 Threads utilisateur (1)
• Dans ce type d’implémentation, le SE n’est pas au courant de
l’existence des threads. Il continue de gérer des processus.
• La gestion des threads est réalisée par l’application (le
processus) au moyen d’une bibliothèque de fonctions.
• Les threads de niveau utilisateur sont indépendants du SE, nous
avons seulement besoin de la bibliothèque permettant leur
emploi.
• Le fait de changer de thread ne requiert aucun appel système
(donc pas de changement du mode utilisateur vers le mode
superviseur).
• Il n’y a donc pas de changement de contexte, ce qui rend la
commutation plus rapide.
26
6. Implémentation du multithreading (3)
6.1 Threads utilisateur (2)
Processus Thread

Espace
utilisateur Table des
threads

Espace Table des


noyau Noyau processus

Le noyau gère les processus (table des processus) et ne se


préoccupe pas de l’existence des threads (modèle plusieurs à un). 27
6. Implémentation du multithreading (4)
6.1 Threads utilisateur (3)

• Les threads utilisateur sont généralement créés, et gérés


rapidement et facilitent aussi la portabilité.
• C’est à l’application qui emploie la bibliothèque de réaliser
l’ordonnancement de l’activité des différents threads, il est
donc possible d’employer un algorithme parfaitement adapté à
cette application précise.
• Le SE seul affecte chaque processus à un processeur, donc
deux threads de niveau utilisateur s’exécutant au sein du
même processus ne pourront pas être déployés sur deux
processeurs différents.

28
6. Implémentation du multithreading (5)
6.2 Threads noyau (1)
• L’intégralité de la gestion des threads est réalisée par le SE.
• On accède aux threads par le biais d’un ensemble d’appels système.
• L’ordonnancement est réalisé non plus sur la base du processus mais
sur celle du thread.
• Le noyau peut répartir les threads sur différents processeurs afin de
réaliser un parallélisme d’exécution.
• Les appels système sont bloquants au niveau du thread et non du
processus
• Le nombre de changements de contexte est multiplié par le nombre
de threads exécutant les mêmes instructions, ce qui peut avoir des
conséquences néfastes sur la rapidité globale de l’application.

29
6. Implémentation du multithreading (6)
6.2 Threads noyau (2)
Processus Thread

Espace
utilisateur

Table des
threads

Espace Table des


noyau Noyau processus

Le noyau se charge de la gestion des threads. Un temps CPU est


alloué à chaque threads (modèle un à un). 30
6. Implémentation du multithreading (7)
6.3 Les threads hybrides (1)
• Les threads utilisateur sont associés à des threads noyau. La
plupart des tâches de gestion s’effectuent sous le mode utilisateur.
• C’est l’application (au travers de son processus) qui se charge de
gérer l’ordonnancement des threads utilisateur ainsi que leur
attachement à un ou plusieurs threads noyau.
• Un ensemble de threads utilisateur est encapsulé dans un
processus léger qui est géré par le SE en tant que processus à part
entière et qui peut donc l’affecter à un processeur particulier.
• C’est au développeur de faire les choix judicieux pour encapsuler
le bon nombre de threads utilisateur dans un processus léger (ils
se partagerons le temps de ce processus léger).

31
6. Implémentation du multithreading (8)
6.3 Threads hybrides (2)
Processus Thread

Espace
utilisateur

Espace
noyau Thread
Noyau noyau

Un ensemble de threads d’un même processus peuvent être associés


à un thread noyau (modèle plusieurs à plusieurs). 32

Vous aimerez peut-être aussi