Verwendung von Atomtypen in C

Jinku Hu 12 Oktober 2023
Verwendung von Atomtypen in C

Dieser Artikel zeigt verschiedene Methoden zur Verwendung von Atomtypen in C.

Verwenden Sie Atomtypen, um Zugriffe auf gemeinsam genutzte Ressourcen implizit zu synchronisieren

Atomic-Objekte sind die einzigen, auf die mehrere Threads gleichzeitig zugreifen und sie ändern können, ohne dass Race-Bedingungen auftreten. Diese Funktion ist für globale und statische Variablen wichtig, auf die von verschiedenen Threads aus zugegriffen wird, und die Programmkorrektheit bleibt erhalten. Im Allgemeinen ist die Verwendung von Objekten vom atomaren Typ eine Alternative zum Sperren von Objekten wie Mutexen und ihren Standard-API-Funktionen wie mtx_lock, mtx_unlock usw. Das folgende Codebeispiel zeigt den einfachen Fall des Zählproblems, bei dem mehrere Threads eine gemeinsam genutzte globale Zählervariable inkrementieren. Schließlich wird die Summe am Ende des Programms auf stdout gedruckt. Beachten Sie, dass wir counter wie gewohnt als int deklariert haben. Leider ist dieses Programm fehlerhaft, obwohl einige Ausführungen zu korrekten Ergebnissen führen können.

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

Ausgabe:

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

Beachten Sie, dass der Hauptthread, der andere mit dem Aufruf thrd_create erstellt, die Zählervariable nicht erhöht. Daher sollte die Summe ein Vielfaches der Konstanten MAX_ITER und NUM_THREADS sein, die die Anzahl der Threads angeben. Man könnte dieses Problem lösen, indem man die Zeile counter += 1 mit Mutex-Sperr- / Entsperrfunktionen oder Semaphoraktionen umgibt. In diesem Fall deklarieren wir jedoch nur counter als Typ von atomic_int. Dieses ganzzahlige Objekt soll nun atomare Eigenschaften haben, was bedeutet, dass jeder Zugriff ohne Unterbrechungen als einzelne Anweisung erfolgt und die sequentielle Ausführung des Programms garantiert.

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

Ausgabe:

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