Utilice tipos atómicos en C

Jinku Hu 12 octubre 2023
Utilice tipos atómicos en C

Este artículo demostrará varios métodos sobre cómo usar tipos atómicos en C.

Utilice tipos atómicos para sincronizar los accesos a los recursos compartidos de forma implícita

Los objetos de tipo atómico son los únicos a los que varios subprocesos pueden acceder y modificar simultáneamente sin que se produzcan condiciones de carrera. Esta característica es esencial para las variables globales y estáticas a las que se accede desde diferentes subprocesos, y se conservará la corrección del programa. Generalmente, el uso de objetos de tipo atómico es una alternativa al bloqueo de objetos como mutex y sus funciones API estándar como mtx_lock, mtx_unlock, etc. El siguiente ejemplo de código demuestra el caso simple del problema de conteo, donde varios subprocesos incrementan una variable de contador global compartida . Finalmente, la suma se imprime en stdout al final del programa. Observe que declaramos contador como el tipo int habitual. Desafortunadamente, este programa tiene fallas a pesar de que algunas ejecuciones pueden producir resultados correctos.

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

Producción :

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

Tenga en cuenta que el subproceso principal que crea otros con la llamada thrd_create no incrementa la variable de contador, por lo que la suma debe ser múltiplo de la constante MAX_ITER y NUM_THREADS que denota el número de subprocesos. Se podría resolver este problema rodeando la línea counter += 1 con funciones de bloqueo / desbloqueo de mutex o acciones de semáforo, pero en este caso, simplemente declaramos counter como el tipo de atomic_int. Este objeto entero ahora se dice que tiene propiedad atómica, lo que implica que cualquier acceso a él ocurrirá sin interrupciones, como una sola instrucción y garantizando la ejecución secuencial del 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);
}

Producción :

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