Use tipos atômicos em C
Este artigo irá demonstrar vários métodos sobre como usar tipos atômicos em C.
Use tipos atômicos para sincronizar acessos a recursos compartilhados implicitamente
Objetos do tipo atômico são os únicos que podem ser acessados e modificados por vários encadeamentos simultaneamente sem ocorrer condições de corrida. Este recurso é essencial para variáveis globais e estáticas acessadas de diferentes threads, e a exatidão do programa será preservada. Geralmente, usar objetos de tipo atômico é uma alternativa para bloquear objetos, como mutexes e suas funções de API padrão, como mtx_lock
, mtx_unlock
etc. O exemplo de código a seguir demonstra o caso simples do problema de contagem, onde vários threads incrementam uma variável de contador global compartilhada . Finalmente, a soma é impressa em stdout
no final do programa. Observe que declaramos counter
como o tipo int
normal. Infelizmente, este programa apresenta falhas, embora algumas execuções possam produzir resultados corretos.
#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);
}
Resultado:
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
Observe que o encadeamento principal que cria outros com a chamada thrd_create
não incrementa a variável do contador, então a soma deve ser múltipla da constante MAX_ITER
e NUM_THREADS
denotando o número de encadeamentos. Pode-se resolver esse problema envolvendo a linha counter += 1
com funções mutex de bloqueio / desbloqueio ou ações de semáforo, mas, neste caso, apenas declaramos counter
como o tipo de atomic_int
. Diz-se agora que este objeto inteiro possui propriedade atômica, implicando que qualquer acesso a ele ocorrerá sem interrupções, como uma única instrução e garantindo a execução sequencial do programa.
#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);
}
Resultado:
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