Rechercher
 

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 :

Fonction mtx_lock (C ISO 2011)

La vidéo


Programmation multi-threads et gestion des accès concurrents

Entêtes à  inclure

#include <threads.h>

Fonction mtx_lock

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.

Paramètre

Valeur de retour

Cette fonction renvoi la valeur thrd_success en cas de succès et thrd_error en cas d'erreur.

Exemple de code

 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;
}
Fichier sample.c : exemple d'utilisation d'un mutext

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.

Fonctions et types connexes

mtx_destroy
mtx_init
mtx_unlock
thrd_create