Utiliser les types atomiques en C

Jinku Hu 12 octobre 2023
Utiliser les types atomiques en C

Cet article présente plusieurs méthodes d’utilisation des types atomiques en C.

Utiliser les types atomiques pour synchroniser implicitement les accès aux ressources partagées

Les objets de type atomique sont les seuls qui peuvent être accédés et modifiés par plusieurs threads simultanément sans se produire des conditions de concurrence. Cette fonctionnalité est essentielle pour les variables globales et statiques accessibles à partir de différents threads, et l’exactitude du programme sera préservée. Généralement, l’utilisation d’objets de type atomique est une alternative au verrouillage d’objets tels que les mutex et leurs fonctions API standard telles que mtx_lock, mtx_unlock etc. L’exemple de code suivant illustre le cas simple du problème de comptage, où plusieurs threads incrémentent une variable de compteur globale partagée. Enfin, la somme est imprimée sur stdout à la fin du programme. Notez que nous avons déclaré counter comme le type int habituel. Malheureusement, ce programme est défectueux même si certaines exécutions peuvent donner des résultats corrects.

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

#ifndef NUM_THREADS
#define NUM_THREADS 4
#endif

int counter = 0;

enum { MAX_ITER = 10000 };

void incrementCounter(void *thr_id) {
  long tid;
  tid = (long)thr_id;
  printf("thread %ld started incrementing ID - %lu\n", tid, thrd_current());

  for (int i = 0; i < MAX_ITER; ++i) {
    counter += 1;
  }

  thrd_exit(EXIT_SUCCESS);
}

int main(int argc, char const *argv[]) {
  thrd_t threads[NUM_THREADS];
  int rc;
  long t;

  for (t = 0; t < NUM_THREADS; t++) {
    rc = thrd_create(&threads[t], (thrd_start_t)incrementCounter, (void *)t);
    if (rc == thrd_error) {
      printf("ERORR; thrd_create() call failed\n");
      exit(EXIT_FAILURE);
    }
  }

  for (t = 0; t < NUM_THREADS; t++) {
    thrd_join(threads[t], NULL);
  }
  printf("count = %d\n", counter);

  thrd_exit(EXIT_SUCCESS);
}

Production:

thread 0 started incrementing ID - 140097636923136
thread 2 started incrementing ID - 140097620137728
thread 1 started incrementing ID - 140097628530432
thread 3 started incrementing ID - 140097611745024
count = 18851

Notez que le thread principal qui en crée d’autres avec l’appel thrd_create n’incrémente pas la variable compteur, donc la somme doit être multiple de la constante MAX_ITER et NUM_THREADS indiquant le nombre de threads. On pourrait résoudre ce problème en entourant la ligne counter += 1 avec des fonctions de verrouillage / déverrouillage de mutex ou des actions de sémaphore, mais dans ce cas, nous déclarons simplement counter comme le type de atomic_int. On dit maintenant que cet objet entier a une propriété atomique, ce qui implique que tout accès à celui-ci se fera sans interruption, en tant qu’instruction unique et garantissant l’exécution séquentielle du programme.

#include <stdatomic.h>
#include <stdio.h>
#include <stdlib.h>
#include <threads.h>
#include <unistd.h>

#ifndef NUM_THREADS
#define NUM_THREADS 4
#endif

atomic_int counter = 0;

enum { MAX_ITER = 10000 };

void incrementCounter(void *thr_id) {
  long tid;
  tid = (long)thr_id;
  printf("thread %ld started incrementing ID - %lu\n", tid, thrd_current());

  for (int i = 0; i < MAX_ITER; ++i) {
    counter += 1;
  }

  thrd_exit(EXIT_SUCCESS);
}

int main(int argc, char const *argv[]) {
  thrd_t threads[NUM_THREADS];
  int rc;
  long t;

  for (t = 0; t < NUM_THREADS; t++) {
    rc = thrd_create(&threads[t], (thrd_start_t)incrementCounter, (void *)t);
    if (rc == thrd_error) {
      printf("ERORR; thrd_create() call failed\n");
      exit(EXIT_FAILURE);
    }
  }

  for (t = 0; t < NUM_THREADS; t++) {
    thrd_join(threads[t], NULL);
  }
  printf("count = %d\n", counter);

  thrd_exit(EXIT_SUCCESS);
}

Production:

thread 0 started incrementing ID - 140125987915520
thread 1 started incrementing ID - 140125979522816
thread 2 started incrementing ID - 140125971130112
thread 3 started incrementing ID - 140125962737408
count = 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