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