Usa i tipi atomici in C

Jinku Hu 12 ottobre 2023
Usa i tipi atomici in C

Questo articolo illustrerà più metodi su come utilizzare i tipi atomici in C.

Usa tipi atomici per sincronizzare implicitamente gli accessi alle risorse condivise

Gli oggetti di tipo atomico sono gli unici a cui è possibile accedere e modificare contemporaneamente da più thread senza che si verifichino condizioni di competizione. Questa funzione è essenziale per le variabili globali e statiche a cui si accede da thread diversi e la correttezza del programma verrà preservata. Generalmente, l’uso di oggetti di tipo atomico è un’alternativa al blocco di oggetti come i mutex e le loro funzioni API standard come mtx_lock, mtx_unlock ecc. Il seguente esempio di codice dimostra il caso semplice del problema del conteggio, dove più threads incrementano una variabile contatore globale condivisa. Infine, la somma viene stampata su stdout alla fine del programma. Notare che abbiamo dichiarato counter come al solito tipo int. Sfortunatamente, questo programma è difettoso anche se alcune esecuzioni possono produrre risultati corretti.

#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);
}

Produzione:

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

Nota che il thread principale che ne crea altri con la chiamata thrd_create non incrementa la variabile counter, quindi la somma dovrebbe essere multipla di MAX_ITER costante e NUM_THREADS che denota il numero di thread. Si potrebbe risolvere questo problema circondando la linea counter += 1 con funzioni di blocco / sblocco mutex o azioni semaforo, ma in questo caso, dichiariamo semplicemente counter come il tipo di atomic_int. Si dice che questo oggetto intero ora abbia proprietà atomiche, il che implica che qualsiasi accesso ad esso avverrà senza interruzioni, come una singola istruzione e garantendo l’esecuzione sequenziale del programma.

#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);
}

Produzione:

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
Autore: 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