Communication inter-Threads en utilisant une FIFO thread-safe

But de l’application

L’application consiste à créer trois threads et à les faire communiquer en utilisant une FIFO thread-safe.

Le premier thread

1
LireFichier();

charge le contenu d’un fichier texte et envoie les caractères lus dans une FIFO.

Le second thread

1
calculer();

récupère les caractères présents dans la première FIFO et convertit les caractères minuscules en majuscule puis les envoie dans une seconde FIFO.

Le troisième thread

1
afficher();

est chargé de récupérer les caractères de la seconde FIFO et de les afficher à l’écran.

Organisation du code


Le programme comporte un fichier

1
main.c

qui contient les corps de 3 fonctions

1
LireFichier(); calculer(); et afficher();

qui vont constituer nos threads.

Les fichiers

1
tfile.h et tfile.c

contiennent les déclarations et les corps des fonctions utilisés par notre FIFO circulaire thread-safe.

Le thread

1
LireFichier();

ouvre un fichier texte, et enfile les caractères lus dans la FIFO jusqu’à la fin du fichier. Lorsque un caractère ne peut pas être enfilé, il sera à nouveau enfilé lors de l’occurrence suivante de la boucle. Lorsque la fin du fichier est atteint, le thread enfile un caractère « null » pour signaler la fin du fichier.

Le thread

1
calculer()

défile les caractères contenus dans la FIFO, convertit les caractères minuscules en majuscule et les enfile dans la seconde FIFO. Lorsque le thread rencontre un caractère « null », il l’enfile puis se termine.

Le thread

1
afficher()

défile les caractères contenus dans le seconde FIFO et les affiche à l’écran. Lorsqu’il rencontre le caractère « null », il se termine.
Notre structure s_file est une file circulaire doublement chaînée dans un tableau. Elle comporte un tableau de caractères, un pointeur de tête et un pointeur de queue. Un mutex permet de délimiter les sections critiques et deux sémaphores sont utilisées comme compteur d ‘entrée et compteur de sortie qui vont permettre de limiter le contenu de la file.

La fonction

1
initQueue()

initialise le sémaphore sem_f avec la valeur de la taille de la file. Le sémaphore sem_e est initialisé à 0. Le mutex et les pointeurs de tête et de queue sont aussi initialisés par cette fonction.

La fonction

1
push()

enfile un caractère. Le sémaphore sem_f est décrémenté à chaque appel. Lorsque la file est pleine, la valeur du sémaphore est nulle et la thread est bloquée. Ceci empêche la file de se remplir au delà de sa taille. L’ajout du caractère dans la file et la modification des pointeurs de tête et de queue sont effectués dans une section critique protégée par le mutex afin que plusieurs modifications simultanées ne soient pas possibles. Ensuite, le sémaphore sem_e est incrémenté. Ce qui va permettre d’indiquer que la file contient des caractères. Finalement, le mutex est débloqué et le thread se termine.

La fonction

1
pull()

défile un caractère. Le sémaphore sem_e est décrémenté lorsque un caractère est défilé et bloque le thread lorsque la file est vide. La lecture et la modification des pointeurs de file sont réalisées dans une section critique protégée par le mutex. La valeur est retournée et le sémaphore sem_f est incrémenté. Le mutex est libéré et le thread se termine.

Diagramme temporel de sem_e

Diagramme temporel de sem_f

Rappel sur les types de sémaphores :

  • Sémaphore binaire : Il est initialisé à 0 et permet de synchroniser deux threads. En effet, une instruction
1
sem_wait();

va interrompre le thread. L’instruction

1
sem_post();

dans un autre thread va libérer le thread interrompu. Nous utilisons un sémaphore binaire sem_e.

  • Sémaphore utilisé comme mutex : Il est initialisé à 1 il se comporte comme un mutex.

Une instruction

1
sem_wait()

débute une section critique et une instruction

1
sem_post()

la termine. Nous n’utilisons pas ce type de sémaphore dans notre programme mais nous utilisons un mutex.

  • Sémaphore utilisé comme compteur : Il est initialisé à une valeur N supérieure à 1. Il protège l’accès à une ressource de taille N. Nous utilisons ce type de sémaphore afin de protéger notre FIFO.

Nous utilisons ici un sémaphore binaire sem_e initialisé à 0.

1
2
sem_t sem_e; //déclaration
sem_init(&t->sem_e,0,0); //initialisation

Ce sémaphore va empêcher que le thread défile un caractère lorsque la file est vide. Il est incrémenté à chaque ajout de caractères dans la file.

Laisser un commentaire

Votre adresse e-mail ne sera pas publiée. Les champs obligatoires sont indiqués avec *