#include <threads.h>
void mtx_destroy( mtx_t * mutex );
La fonction mtx_destroy
permet de libérer les ressources acquises par 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.
Cette fonction utilise un objet de type mtx_t
qui correspond à l'instance de mutex à libérer.
Libérer un mutex alors que des threads sont en attentes sur cette ressource provoque un comportement non défini.
mutex : un pointeur vers une donnée de type mtx_t
qui correspond au mutex à libérer.
Cette fonction ne renvoi pas de valeur de retour.
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 :