1. Synchronisation
|
Pour la compilation un binaire utilisant la librairie pthread il est nécessaire d’ajouter de l’ajouter à l’édition des liens.
|
1.1. Exclusion mutuelle
1.1.1. Création d’un MUTEX
La création d’un MUTEX est réalisé via l’utilisation de la macro PTHREAD_MUTEX_INITIALIZER.
1
2
#include <pthread.h>
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; // Create a mutex
1.1.2. Réservation d’un MUTEX
La réservation d’un MUTEX est réalisé via l’utilisation de la fonction pthread_mutex_lock.
Si la resource est déjà réservée, la fonction pthread_mutex_lock retiendra l’éxecution du programme tant
que le mutex n’est pas libéré.
1
2
3
#include <pthread.h>
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; // Création
pthread_mutex_lock(&mutex); // Réservation (appel bloquant)
1.1.3. Libération d’un MUTEX
La libération d’un MUTEX est réalisé via l’utilisation de la fonction pthread_mutex_unlock.
L’appel à cette fonction permet la libération d’un éventuel thread en attente de la resource
(qui serait bloqué par le fonction pthread_mutex_lock).
Si la resource n’est pas réservé, l’appel n’a aucun effet.
1
2
3
#include <pthread.h>
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; // Création
pthread_mutex_unlock(&mutex); // Libération de la resource
1.1.4. Utilisation
Compilez et exécuter le programme suivant :
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <pthread.h>
#include <unistd.h>
#define NB_THREADS 5
typedef struct {
int n;
} data;
void print_char_by_char(char *s, int n) {
int i = 0;
while (s[i] != '\0') {
printf("%c", s[i]);
fflush(stdout); // Forcing stdout to be displayed
i++;
// Sleep for 0.5 second
struct timespec tim, tim2;
tim.tv_sec = 0;
tim.tv_nsec = 500000000L;
nanosleep(&tim , &tim2);
}
printf("%d \n", n);
sleep(1);
}
void *mytask(void *p_data) {
char *message = "Thread_n ";
data *info = p_data;
print_char_by_char(message, info->n);
return NULL;
}
int main(void) {
printf("main start\n");
int i;
pthread_t threads[NB_THREADS];
data infos[NB_THREADS];
for (i = 0; i < NB_THREADS; i++) {
infos[i].n = i;
pthread_create(&threads[i], NULL, mytask, &infos[i]);
}
for (i = 0; i < NB_THREADS; i++) {
pthread_join(threads[i], NULL);
}
printf("main end\n");
}
Question : Expliquez ce qu’affiche la sortie standard.
Question : En utilisant le système des mutex, modifiez le programme pour que le message s’affiche correctement.
1.2. Semaphores
Contrairement au système de thread utilisé précédemment, le système de gestion
des sémaphores présenté dans ces TP n’est pas issue de la librairie pthread.
La différence principale se trouve dans la portée des sémaphores :
-
Les sémaphores pthreads ne sont utilisable par les threads d’un programme.
-
Les sémaphores systèmes sont utilisable par plusieurs programmes ayant un lien de parenté ou non (
fork).
1.2.1. Création d’un sémaphore
La création d’un sémaphore se fait par l’utilisation de la fonction sem_open.
1
2
3
4
5
6
7
#include <semaphore.h>
sem_t *semaphore = sem_open(
"mon-semaphore", (1)
O_CREAT|O_EXCL, (2)
0777, (3)
1 (4)
);
| 1 | Nom du sémaphore |
| 2 | Options de création (dans notre cas: création s’il n’existe pas et erreur s’il existe) |
| 3 | les droits d’accès au sémaphore |
| 4 | la valeur initiale du sémaphore s’il est créé |
sem_open créé un sémaphore système, s’il n’est pas supprimé par le programme
il sera présent lors de la prochaine exécution du programme.
L’utilisation de perror permet de récupérer l’erreur.
Dans ce cas, la constante SEM_FAILED est retournée.
|
1.2.2. Réservation d’un sémaphore
La réservation d’un sémaphore s’effectue via l’appel système sem_wait.
Il permet de décrémenter le compteur du sémaphore.
S’il tombe à zéro alors l’appel est bloquant.
1
2
3
4
#include <semaphore.h>
sem_t *semaphore = sem_open( "mon-semaphore", O_CREAT|O_EXCL, 0777, 1 );
sem_wait(semaphore); // l'appel n'est pas bloquant car le sémaphore est initialisé à 1
sem_wait(semaphore); // l'appel est bloquant car le compteur du sémaphore est 0
1.2.3. Libération d’un sémaphore
La libération d’un sémaphore s’effectue par l’appel système sem_post.
Il permet d’incrémenter le compteur du sémaphore
et de libérer les processus en attente de la resource.
1
2
3
#include <semaphore.h>
sem_t *semaphore = sem_open( "mon-semaphore", O_CREAT|O_EXCL, 0777, 0);
sem_post(semaphore); // le compteur est incrémenté et est égal à 1
1.2.4. Suppression d’un sémaphore
La suppression d’un sémahore se fait par l’appel système suivant:
1
2
3
4
#include <semaphore.h>
sem_t *semaphore = sem_open( "mon-semaphore", O_CREAT|O_EXCL, 0777, 1 );
sem_close(semaphore); (1)
sem_unlink("mon-semaphore"); (2)
| 1 | Fermeture du sémaphore, le programme ne peut plus l’utiliser |
| 2 | Suppression système du sémaphore. |
1.2.5. Utilisation
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <pthread.h>
#include <unistd.h>
#define NB_THREADS 5
typedef struct {
int n;
} data;
void print_char_by_char(char *s, int n) {
int i = 0;
while (s[i] != '\0') {
printf("%c", s[i]);
fflush(stdout); // Forcing stdout to be displayed
i++;
// Sleep for 0.5 second
struct timespec tim, tim2;
tim.tv_sec = 0;
tim.tv_nsec = 500000000L;
nanosleep(&tim , &tim2);
}
printf("%d \n", n);
sleep(1);
}
void *mytask(void *p_data) {
char *message = "Thread_n ";
data *info = p_data;
print_char_by_char(message, info->n);
return NULL;
}
int main(void) {
printf("main start\n");
int i;
pthread_t threads[NB_THREADS];
data infos[NB_THREADS];
for (i = 0; i < NB_THREADS; i++) {
infos[i].n = i;
pthread_create(&threads[i], NULL, mytask, &infos[i]);
}
for (i = 0; i < NB_THREADS; i++) {
pthread_join(threads[i], NULL);
}
printf("main end\n");
}
Question : En utilisant le système des sémaphores, modifiez le programme pour que le message s’affiche correctement.
1.2.6. Barrière de synchronisation
Question :
Écrivez un programme qui lance 10 threads et attend la fin de tous les threads.
Chaque thread devra :
-
Exécuter la même fonction
-
Attendre un temps aléatoire (
sleep()) -
Attendre la fin des autres threads (à l’aide de sémaphores)
|