#include <threads.h>
int mtx_lock( mtx_t * mutex );
La fonction mtx_lock
permet de verrouiller un mutex. Le terme de mutex est un raccourci vers mutual exclusion.
Un mutex est donc un mécanisme de synchronisation entre plusieurs threads qui cherchent à accéder, en parallèle, à une ressource partagée.
Dans ce cas, ces accès concurrents peuvent amener à des incohérences si vous ne les synchronisez pas.
mutex : un pointeur vers une donnée de type mtx_t
qui correspond au mutex à verrouiller.
Cette fonction renvoi la valeur thrd_success
en cas de succès et thrd_error
en cas d'erreur.
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 |
#include <stdio.h> #include <stdlib.h> #include <threads.h> #define THREAD_COUNT 10 #define THREAD_LOOP 1000000 // Une ressource partagée sur laquelle synchroniser nos threads. mtx_t mutex; unsigned long counter = 0; // Définition de la fonction sur laquelle amorcer tous nos threads. int threadFunction(void* data) { for( int i = 0; i<THREAD_LOOP; i++ ) { mtx_lock( &mutex ); counter ++; mtx_unlock( &mutex ); } printf( "Thread %ld terminé\n", (long) data ); return thrd_success; } int main() { mtx_init( &mutex, mtx_plain ); // On démarre tous nos threads sur la fonction définie ci-dessus. thrd_t threads[THREAD_COUNT]; for( long i=0; i<THREAD_COUNT; i++ ) { thrd_create( &threads[i], threadFunction, (long *)i ); } // On attend la terminaison de tous nos threads. for( int i=0; i<THREAD_COUNT; i++ ) { thrd_join( threads[i], NULL ); } // Affichage du compteur et libération du mutex. printf( "Counter == %ld\n", counter ); mtx_destroy( &mutex ); return EXIT_SUCCESS; } |
Pour compiler cet exemple sous environnement Linux/Unix, il est nécessaire de lier la librairie pthread (Posix Thread) à votre exécutable. Voici un exemple de compilation.
$> gcc -o sample sample.c -lpthread $> ./sample Thread 2 terminé Thread 4 terminé Thread 0 terminé Thread 1 terminé Thread 8 terminé Thread 7 terminé Thread 6 terminé Thread 5 terminé Thread 3 terminé Thread 9 terminé Counter == 10000000 $>
Normalement l'affichage de la valeur de compteur devrait être 10 000 000 (THREAD_COUNT
* THREAD_LOOP
).
Maintenant pour bien comprendre l'intérêt du mutex, je vous demande de commenter les lignes 9, 17, 19, 29 et 44 (celles relatives à la gestion du mutex).
Relancez plusieurs fois votre programme : normalement, la valeur du compteur devient imprédictible et donc on peut dire que notre programme ne marche plus.
Cela est dû au fait que l'instruction counter ++;
, en ligne 18, n'est pas atomique. Elle correspond en vrai à trois instructions en langage
machine : on charge la valeur de la variable en mémoire vers un registre du CPU, on incrémente la valeur de ce registre, et on recopie ce registre dans la
zone mémoire associée à votre variable counter
. En conséquence, imaginez qu'un premier thread arrive jusqu'à incrémenter la valeur du registre
mais qu'il soit suspendu à ce moment-là, si un autre thread cherche aussi à incrémenter la variable counter
, il ne constatera pas l'incrément
précédent, vu qu'en mémoire on a toujours la valeur précédente. Si on utilise le mutex, l'incrément est donc garanti comme ne pouvant être joué que par un
unique thread à la fois.
Améliorations / Corrections
Vous avez des améliorations (ou des corrections) à proposer pour ce document : je vous remerçie par avance de m'en faire part, cela m'aide à améliorer le site.
Emplacement :
Description des améliorations :