Utiliser Mutex Lock en C

Jinku Hu 12 octobre 2023
Utiliser Mutex Lock en C

Cet article explique plusieurs méthodes d’utilisation du verrouillage mutex dans C.

Utilisez le type pthread_mutex_t et la fonction pthread_mutex_lock pour protéger la section critique du code

Les threads partagent des espaces d’adressage, ce qui implique que les modifications des données partagées telles que les variables globales doivent être synchronisées; sinon, il y aura un comportement de programme incorrect. Notez que le code suivant crée 4 threads supplémentaires avec l’appel pthread_create et passe func3 comme point de départ de leur exécution. func3 modifie la variable globale shared avec une par une dans une boucle for d’itération de 10000. Ainsi, si les quatre threads incrémentent la valeur de shared de 10000 chacun, le programme devrait afficher 40000.

Si vous exécutez le code suivant, le résultat sera un entier aléatoire, mais pas 40000. Ce comportement est généralement classé comme une condition de concurrence, ce qui implique que les threads donnés accèdent à la variable partagée sans se consulter, c’est-à-dire la synchronisation. À savoir, souvent lorsque leur exécution de la boucle s’entrelace, l’incohérence est atteinte dans les accès et les magasins de la variable partagée, et finalement, une somme incorrecte est produite.

La section de code où plusieurs threads modifient le même objet dans la mémoire est appelée une section critique. En règle générale, la section critique doit être protégée par un type de verrou qui forcerait les autres threads à attendre que le thread actuel termine l’exécution et s’assure qu’ils obtiennent tous la valeur incrémentée correcte. Mutex est l’un des types de verrous qui peuvent être utilisés pour protéger la section critique comme cette boucle for dans func3.

#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

#ifndef NUM_THREADS
#define NUM_THREADS 4
#endif

int shared = 0;

void* func3(void* param) {
  printf("Incrementing the shared variable...\n");
  for (int i = 0; i < 10000; ++i) {
    shared += 1;
  }
  return 0;
}

int main() {
  pthread_t threads[NUM_THREADS];

  for (int i = 0; i < NUM_THREADS; ++i) {
    pthread_create(&threads[i], NULL, func3, NULL);
  }

  for (int i = 0; i < NUM_THREADS; ++i) {
    pthread_join(threads[i], NULL);
  }

  printf("%d\n", shared);
  exit(EXIT_SUCCESS);
}

Production:

Incrementing the shared variable...
Incrementing the shared variable...
Incrementing the shared variable...
Incrementing the shared variable...
30384

Dans ce cas, nous utiliserons la bibliothèque de threads POSIX et son type intégré pthread_mutex_t. La variable de type pthread_mutex_t est généralement déclarée comme durée de stockage static. Mutex ne doit être initialisé qu’une seule fois avant d’être utilisé. Lorsque le mutex est déclaré comme static, il faut utiliser la macro PTHREAD_MUTEX_INITIALIZER pour l’initialiser. Une fois le mutex initialisé, les threads peuvent utiliser les fonctions pthread_mutex_lock et pthread_mutex_unlock en conséquence. pthread_mutex_lock verrouille l’objet mutex passé comme seul argument. Si le mutex était déjà verrouillé, le thread appelant est bloqué jusqu’à ce que le mutex devienne disponible. pthread_mutex_unlock doit être appelé pour déverrouiller le mutex. S’il y a des threads en attente sur le même mutex, la politique de planification détermine lequel obtient le verrou d’objet. Enfin, nous appelons pthread_join sur chacun des quatre threads et imprimons l’entier - shared, qui dans ce cas devrait avoir la valeur correcte stockée.

#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

#ifndef NUM_THREADS
#define NUM_THREADS 4
#endif

int shared = 0;
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;

void* func3(void* param) {
  pthread_mutex_lock(&mutex);
  printf("Incrementing the shared variable...\n");
  for (int i = 0; i < 10000; ++i) {
    shared += 1;
  }
  pthread_mutex_unlock(&mutex);
  return 0;
}

int main() {
  pthread_t threads[NUM_THREADS];

  for (int i = 0; i < NUM_THREADS; ++i) {
    pthread_create(&threads[i], NULL, func3, NULL);
  }

  for (int i = 0; i < NUM_THREADS; ++i) {
    pthread_join(threads[i], NULL);
  }

  printf("%d\n", shared);
  exit(EXIT_SUCCESS);
}

Production:

Incrementing the shared variable...
Incrementing the shared variable...
Incrementing the shared variable...
Incrementing the shared variable...
40000
Auteur: Jinku Hu
Jinku Hu avatar Jinku Hu avatar

Founder of DelftStack.com. Jinku has worked in the robotics and automotive industries for over 8 years. He sharpened his coding skills when he needed to do the automatic testing, data collection from remote servers and report creation from the endurance test. He is from an electrical/electronics engineering background but has expanded his interest to embedded electronics, embedded programming and front-/back-end programming.

LinkedIn Facebook