Notion de threads
1 Introduction
Nous allons aborder les threads dans leur standard PoSiX. Il existe bien d’autres librairies de
threads mais celle-ci a l’avantage de représenter une norme internationale soutenue par nombre d’uni-
versités et d’entreprises.
1.1 Utiliser la librairie [Link]
Pour utiliser cette librairie il faut indiquer dans la ligne de commande de compilation la directive
-lpthread. Celle-ci indique tout simplement que vous incluez la librairie [Link].
Par exemple pour compiler le fichier permut.c qui contient des instructions pthreads en un fichier
objet permut.o vous devez écrire gcc -c -lpthread permut.c
2 Deux threads d’affichage
Le programme suivant crée 2 threads qui vont afficher 1000 fois le caractère ’a’ et ’b’ respective-
ment.
#include <stdio.h>
#include <pthread.h>
void * process(void * arg){ // Un seul argument de type void *
int i;
fprintf(stderr, "Starting process %s\n", (char *) arg);
for (i = 0; i < 10000; i++) { // Ecrit sur la sortie standard
write(1, (char *) arg, 1);
}
return NULL;
}
int main(void){
int retcode;
pthread_t th_a, th_b;
void * retval;
// création de la thread th_a
retcode = pthread_create(&th_a, NULL, process, (void *) "a");
if (retcode != 0) fprintf(stderr, "create th_a failed %d\n", retcode), exit(1);
// création de la thread th_b
retcode = pthread_create(&th_b, NULL, process, (void *) "b");
if (retcode != 0) fprintf(stderr, "create th_b failed %d\n", retcode), exit(1);
1
// attente de la terminaison de la thread th_a
retcode = pthread_join(th_a, &retval);
if (retcode != 0) fprintf(stderr, "join a failed %d\n", retcode), exit(1);
// attente de la terminaison de la thread th_b
retcode = pthread_join(th_b, &retval);
if (retcode != 0) fprintf(stderr, "join b failed %d\n", retcode), exit(1);
return 0;
}
Le programme principal crée avec la commande pthread create( &th a, NULL, process, (void
*) ’a’ ) la thread th a et lui associe la fonction process qui prend en argument le caractère ’a’. Si
la création a pu se faire la valeur retournée par ptread create est 0. Ensuite le programme principal
attent la terminaison de la thread th a avec l’instruction pthread joint( &th a, ret val ) qui
retourne également la valeur 0 si tout c’est bien passée.
Il fait de même avec la thread th b et le caractère ’b’.
3 Trois threads d’incrémentation
Le programme crée un tabelau de 3 threads qui incrémentent un tableau de 3 entiers. Cette variable
est globale aux 3 threads. L’indice des threads dans le tableau des threads permet à chaque thread de
savoir quel élément du tabelau elle doit incrémenter.
Ici les threads sont tuées brusquement avec la fin du processus !
#include <pthread.h>
#include <stdio.h>
#include "outils.h"
int val[3] ; // tabelau en global de 3 entiers
// déclaration de la fonction associée aux threads
void * thread (void *) ;
int main (int c, char *v[]) {
int num, i ;
// tabelau des 3 threads
pthread_t pthread_id[3] ;
// structure regroupant les attributs des threads
pthread_attr_t pthread_attr ;
int res ;
// récupération des attributs des threads
pthread_attr_init( &pthread_attr ) ;
// choix du RR comme répartiteur
pthread_attr_setschedpolicy( &pthread_attr, SCHED_RR ) ;
2
// creation de 3 threads qui executent toute la fct thread
// mais avec le numero de la thread en parametre!!!
for( num=0 ; num<3 ; num++ )
{
res = pthread_create( pthread_id+num, &pthread_attr,
thread, ( void * ) num ) ;
if( res != 0 ) syserr( "thread_create" ) ;
}
for( i=0 ; i<3000 ; i++ )
// affiche la valeur pour chacune des threads
printf( "%d %d %d\n", val[0], val[1], val[2] ) ;
exit( 0 ) ;
}
void * thread (void *num) {
for( ; ; ) // incrementation de la valeur associée à cette thread
val[(int)num]++ ;
}
4 Toujours plus !
Le programme principal peut aussi créer 2000 threads et attendre leurs fins avant de terminer
lui-même.
#include <errno.h>
#include <stdio.h>
#include <pthread.h>
void * test_thread (void *cpt) {
unsigned int count = (unsigned int) *cpt ;
fprintf( stdout, "le compteur vaut %d\n", count ) ;
sleep( 5 ) ; // une pause
return NULL;
}
int main (void) {
unsigned long count;
setvbuf (stdout, NULL, _IONBF, 0);
// Mettre le buffer à vide pour écrire sans tampon
for (count = 0; count < 2000; ++count) {
pthread_t thread;
int status;
3
status = pthread_create (&thread, NULL, test_thread, ( void * ) &count );
if (status != 0) {
fprintf ( stderr,
"status = %d, count = %lu: %s\n",
status, count,strerror (errno));
exit( 1 );
}
else {
printf ("count = %lu\n", count);
}
pthread_join (thread, NULL); // Attente de la thread
usleep (10); // une pause
}
return 0;
}
5 Exercices
1. À l’aide de threads, proposez un module de multication matricielle.
2. Toujours à l’aide de threads, proposez un module de lissage d’une image par le masque suivant :
1/9 1/9 1/9
1/9 1/9 1/9
1/9 1/9 1/9
Le résultat sera stoché dans uen nouvelle image. Appliquer ce masque à une image revient à
sur-imposer ce masque à l’image, à multiplier les pixels surimposés par les valeurs du masque,
en faire la somme et changer la valeur du pixel central (par rapport au masque) par le résultat
de la somme.
Une image test vous sera fournie pour laquelle vous devrez faire un thread permettant la lecture
de son fichier. Son format commence par 3 lignes de commentaires suivies de la matrice de
caractères contenant l’image. La deuxième ligne indique la dimension en lignes, colonnes de
l’image. La troisième ligne indique la valeur maximale du niveau de gris.
3. Écrivez le filtrage médian d’une image !
Voici la fonction de lecture d’une image que vous aurez besoin avant toute chose. vous pouvez créer
une thread de lecture ! L’image qui sera alors stockée dans le tabelau in image de taille dimx × dimy
et de type char.
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
// taille d’une image 512x512...
#define STDSIZE 262144
#define STDDIMX 512
#define STDDIMY 512
4
#define HALFSTDDIMX 255
#define HALFSTDDIMY 255
main (int argc, char *argv[]) {
FILE *f;
char comment[BUFSIZ];
char picture_type[2];
char in_file_name[20];
char *in_image;
int dimx, dimy, nvg, in_image_size;
// ouverture du fichier image en mode lecture...
if ((f=fopen(argv[1],"r"))==NULL)
fprintf (stderr,"Erreur a l’ouverture du fichier : %s\n", argv[1]), exit (-1);
// verifier que c’est une image PGM... ignorer l’entete
fscanf (f,"%s\n",picture_type);
if (strcmp(picture_type,"P5"))
fprintf (stderr,"Erreur : ce n’est pas un fichier pgm\n"), exit (-1);
fgets (comment,BUFSIZ,f); // lecture du commentaire xv...
// lecture des dimensions...
fscanf (f,"%d %d\n",&dimx,&dimy);
// lecture du nbre de niveau de gris...
fscanf (f,"%d\n",&nvg);
/* reservation memoire et lecture de l’image */
in_image_size = dimx*dimy;
in_image = (char *) calloc (in_image_size,sizeof(char));
fread (in_image,sizeof(char),in_image_size,f);
fclose (f);
}