Si l'on veut exécuter plusieurs tâches en parallèle, on peut utiliser des processus différents
Pour exécuter plusieurs instances de la même tâche en parallèle, on peut utiliser des threads (processus légers) à la place des processus
text et data du processus, ainsi que le tas#include <pthread.h>
int pthread_create(pthread_t *thread, const pthread_attr_t *attr, void *(*start_routine)(void *), void *arg)
int pthread_join(pthread_t thread, void **value_ptr)
void pthread_exit(void *value_ptr)
La bibliothèque pthread permet de créer et gérer des threads dans un processus
pthread_create pour démarrer un nouveau thread
start_routine est la fonction à exécuter dans le threadstart_routine prend un unique argument arg de type void*pthread_join pour attendre la fin d'un thread en cours
value_ptr est un pointeur où écrire le résultat de la fonction du thread qui a terminépthread_exit permet de terminer un thread (appelée automatiquement si la fonction start_routine termine)#include <stdio.h> #define NB_CASES 1000 void tache(int *tab) { int i; for (i = 0; i < NB_CASES; i++) { tab[i]=i*i; } }#include <stdio.h> #define NB_CASES 1000 #define NB_THREADS 4 void tache(int deb, int fin, int *tab) { int i; for (i = deb; i < fin; i++) { tab[i]=i*i; } }#include <pthread.h> #include <stdio.h> #define NB_CASES 1000 #define NB_THREADS 4 struct ThreadArgs { int debut; int fin; int *tab; }; void* tache(void* args) { struct ThreadArgs *a = args; int i; for (i = a->debut; i < a->fin; i++) { a->tab[i]=i*i; } return NULL; }
int main(void) { int i, tab[NB_CASES]; tache(tab); for (i = 0; i < NB_CASES; i++) { printf("%d ", tab[i]); } return 0; }int main(void) { int i, tab[NB_CASES]; for (i = 0; i < NB_THREADS; i++) { debut = i * NB_CASES / NB_THREADS; fin = (i+1) * NB_CASES / NB_THREADS; tache(debut, fin, tab); } for (i = 0; i < NB_CASES; i++) { printf("%d ", tab[i]); } return 0; }int main(void) { int i, tab[NB_CASES]; struct ThreadArgs args[NB_THREADS]; pthread_t threads[NB_THREADS]; for (i = 0; i < NB_THREADS; i++) { args[i].debut = i * NB_CASES / NB_THREADS; args[i].fin = (i+1) * NB_CASES / NB_THREADS; args[i].tab = tab; pthread_create(&threads[i], NULL, tache, &args[i]); } for (i = 0; i < NB_THREADS; i++) { pthread_join(threads[i], NULL); } for (i = 0; i < NB_CASES; i++) { printf("%d ", tab[i]); } return 0; }
#include <pthread.h>
#include <stdio.h>
#define NB_THREADS 4
void* incr(void *arg) {
int i;
int *p = arg;
for (i = 0; i < 10000; i++) {
(*p)++;
}
return NULL;
}
int main(void) {
int i, c=0;
pthread_t threads[NB_THREADS];
for (i = 0; i < NB_THREADS; i++) {
pthread_create(&threads[i], NULL, incr, &c); }
for (i = 0; i < NB_THREADS; i++) {
pthread_join(threads[i], NULL); }
printf("%d\n", c);
return 0;
}
Les threads partagent le même espace mémoire
#include <pthread.h>
int incr() {
static int c = 0;
static pthread_mutex_t mutex;
mutex = PTHREAD_MUTEX_INITIALIZER;
// bloquer le verrou
pthread_mutex_lock(&mutex);
c++;
int r = c; // sauvegarder résultat
// libérer le verrou
pthread_mutex_unlock(&mutex);
return r;
}
// cette fonction est réentrante void echange(int *x, int *y) { int t; t = *x; *x = *y; *y = t; }int t; // cette fonction n'est pas réentrante void echange(int *x, int *y) { t = *x; *x = *y; *y = t; }int t; // cette fonction est réentrante void echange(int *x, int *y) { int s = t; t = *x; *x = *y; *y = t; t = s; }
Une fonction est dite réentrante si elle se comporte correctement lorsqu'elle est appelée pendant une exécution d'elle-même
$ gcc prog.c -lpthread -D_REENTRANT$ gcc prog.c -pthread
pthread, il faut demander au compilateur de lier l'exécutable à la bibliothèque : -lpthread-D_REENTRANTstrtok_r au lieu de strtok)getc et putc)errno-pthread se charge d'activer les options nécessaires, spécifiques au système courant (c'est la solution à préférer)-lpthread -D_REENTRANT