Systèmes d'Exploitation II - Travaux Dirigés II : Le Multithreading

Ce travaux dirigés porte sur le multithreading dans les systèmes d'exploitation. Il permet de comprendre les concepts fondamentaux du multithreading, ses différences avec la multiprogrammation, ainsi que la gestion des threads en programmation avec la bibliothèque pthread.

D'après le document Systèmes d'Exploitation II - Travaux Dirigés II : Le Multithreading

Cet article a été rédigé automatiquement à partir du document source, puis vérifié avant publication.

Document source

Systèmes d'Exploitation II - Travaux Dirigés II : Le Multithreading

Multithreading, Programming, Operating Systems · PDF · 2 pages

Afficher l'aperçu du document

Consulter le document original →

Ce travaux dirigés porte sur le multithreading dans les systèmes d'exploitation. Il permet de comprendre les concepts fondamentaux du multithreading, ses différences avec la multiprogrammation, ainsi que la gestion des threads en programmation avec la bibliothèque pthread. Le TP inclut des exercices pratiques pour créer, lancer et synchroniser des threads, ainsi qu'un projet de génération multithreadée de la suite de Fibonacci. Pour réaliser ce TP, il est nécessaire de disposer d'un environnement de développement supportant la bibliothèque pthread, ainsi que de connaissances de base en programmation C.

Objectifs

  • Comprendre la notion de multithreading et ses différences avec la multiprogrammation.
  • Identifier les différences et similarités entre un processus et un thread.
  • Connaître la structure d'un Thread Control Block (TCB) et son rôle.
  • Écrire des programmes utilisant la bibliothèque pthread pour créer et gérer des threads.
  • Mettre en œuvre des threads partageant des données et comprendre les problèmes liés à la concurrence.
  • Développer un programme multithreadé générant la suite de Fibonacci.

Prérequis et installation

  • Connaissances de base en programmation C.
  • Compréhension des concepts de processus et threads.
  • Environnement de développement avec support de la bibliothèque pthread (Linux, macOS ou environnement compatible).
  • Compilateur C avec option de liaison pthread (exemple : gcc -pthread).

Notions théoriques sur le multithreading

Le multithreading consiste à exécuter plusieurs threads au sein d'un même processus, partageant la mémoire et les ressources. Il se distingue de la multiprogrammation où plusieurs processus s'exécutent simultanément mais isolés en mémoire.

Un thread est plus léger qu'un processus, car il partage le même espace mémoire que les autres threads du processus. Les processus ont chacun leur propre espace mémoire. Dans une application où les données sont partagées en mémoire, il est préférable d'utiliser des threads plutôt que des processus pour faciliter la communication et réduire la surcharge.

Le TCB (Thread Control Block) est une structure de données qui contient les informations nécessaires à la gestion d'un thread, similaire au PCB (Process Control Block) pour les processus. Le TCB inclut notamment l'état du thread, ses registres, sa pile, et d'autres informations de contexte.

Analyse d'un exemple de code multithreadé problématique

Considérez les deux fonctions suivantes exécutées par deux threads différents, partageant la variable globale pointer :

int *pointer = NULL;

void * writer_thread() {
  while (1) {
    if (pointer == NULL) {
      pointer = malloc(sizeof(int));
      *pointer = rand();
    }
  }
}

void *reader_thread() {
  while (1) {
    if (pointer != NULL) {
      printf("pointer = %d", *pointer);
      free(pointer);
      pointer = NULL;
    }
  }
}

Lors de l'exécution simultanée de ces deux threads, une erreur de segmentation apparaît après un certain temps. Cette erreur provient d'un problème de concurrence sur la variable pointer. En effet, les deux threads accèdent et modifient pointer sans synchronisation, ce qui peut entraîner des accès à une zone mémoire libérée ou non encore allouée. Ce comportement non protégé provoque l'erreur de segmentation.

Création et lancement de threads avec pthread

La création et le lancement d'un thread se font avec la fonction pthread_create. Voici un exemple de code :

pthread_t th1;
int ret;

ret = pthread_create(&th1, NULL, runDuThread, "1");
if (ret != 0) {
  fprintf(stderr, "pthread_create error 1");
  exit(0);
}

Le thread exécute la fonction runDuThread dont le prototype est :

void* runDuThread(void *param);

Le paramètre param est un pointeur passé en argument lors de la création du thread (ici la chaîne "1"). Il permet de transmettre des données au thread.

Lorsque la fonction main se termine, tous les threads lancés se terminent également. Pour éviter cela, il faut attendre la fin des threads avant de terminer le programme en utilisant :

(void) pthread_join(th1, (void *)&ret);

Le paramètre ret recevra la valeur retournée par la fonction pthread_exit(int val) appelée avant la terminaison du thread.

Exercices pratiques

Exercice 1 : Affichage des lettres minuscules et majuscules

Écrire un programme qui crée deux threads :

  • Le premier affiche les 26 lettres minuscules de l'alphabet.
  • Le second affiche les 26 lettres majuscules de l'alphabet.

Chaque thread doit exécuter une fonction spécifique qui réalise cet affichage.

Exercice 2 : Incrémentation concurrente d'une variable globale

Écrire un programme qui initialise une variable globale à 0, puis crée deux threads. Chaque thread incrémente cette variable N fois. À la fin de l'exécution de chaque thread, afficher la valeur finale de la variable.

Ce programme permet d'observer les effets de la concurrence sur une variable partagée sans mécanisme de synchronisation.

Génération multithreadée de la suite de Fibonacci

La suite de Fibonacci est définie par :

fib[0] = 0
fib[1] = 1
fib[n] = fib[n-1] + fib[n-2]

Écrire un programme multithreadé qui génère la suite de Fibonacci. Le fonctionnement attendu est le suivant :

  • L'utilisateur saisit le nombre d'éléments de la suite à générer.
  • Le programme crée un thread séparé chargé de calculer les nombres de Fibonacci et de les stocker dans une structure de données partagée (un tableau).
  • Lorsque le thread a terminé son exécution, le thread principal affiche la séquence calculée.

Résultats attendus

  • Compréhension claire des différences entre processus et threads.
  • Programme affichant correctement les lettres minuscules et majuscules via deux threads.
  • Programme montrant la valeur finale de la variable globale après incrémentations concurrentes (avec ou sans effets de concurrence selon la synchronisation).
  • Programme générant la suite de Fibonacci avec un thread dédié, affichant la séquence correcte après calcul.
  • Identification et explication des erreurs de segmentation dues à un accès concurrent non protégé à une variable partagée.

Pièges courants

  • Ne pas synchroniser l'accès aux variables partagées entre threads, ce qui peut provoquer des erreurs de segmentation ou des résultats incohérents.
  • Oublier de vérifier le code de retour de pthread_create, ce qui peut masquer des erreurs de création de threads.
  • Terminer la fonction main avant la fin des threads, ce qui arrête prématurément le programme et les threads.
  • Ne pas utiliser pthread_join pour attendre la fin des threads, ce qui peut entraîner des comportements imprévisibles.
  • Dans l'exemple avec pointer, ne pas protéger les accès à la variable partagée peut provoquer des accès à une mémoire libérée.

Partager

Commentaires

Aucun commentaire pour le moment. Posez la première question.

Les commentaires sont relus avant publication. Votre e-mail n'est jamais affiché.

← Toutes les révisions