Verwendung von thread_local-Variablen in C

Jinku Hu 12 Oktober 2023
  1. Verwenden Sie den Typ _Thread_local, um eine Variable mit Thread-Speicherdauer zu deklarieren
  2. Verwenden Sie den Typ thread_local, um eine Variable mit Thread-Speicherdauer zu deklarieren
Verwendung von thread_local-Variablen in C

In diesem Artikel werden verschiedene Methoden zur Verwendung von thread_local-Variablen in C erläutert.

Verwenden Sie den Typ _Thread_local, um eine Variable mit Thread-Speicherdauer zu deklarieren

Die Sprache C definiert mehrere Schlüsselwörter für verschiedene Speicherklassen wie auto, static, register, extern. Seit der Spezifikation des C11-Standards wurde der Spezifizierer _Thread_local hinzugefügt. Die Speicherdauer _Thread_local beginnt zum Zeitpunkt der Thread-Erstellung und endet mit deren Beendigung. Der im Objekt _Thread_local gespeicherte Wert wird beim Starten des Threads initialisiert und beim Beenden des Threads bereinigt. Im Allgemeinen sind die threadlokalen Objekte eine weitere Alternative, um Race-Bedingungen in gemeinsam genutzten Ressourcen zu vermeiden. Wir trennen die Daten implizit zwischen Threads, da qualifizierte Objekte mit dem Namen _Thread_local für jeden Thread separate Instanzen haben.

#include <stdatomic.h>
#include <stdio.h>
#include <stdlib.h>
#include <threads.h>

#ifndef NUM_THREADS
#define NUM_THREADS 4
#endif

_Thread_local 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;
  }

  return (void *)counter;
}

int main(int argc, char const *argv[]) {
  thrd_t threads[NUM_THREADS];
  int rc, sum = 0;

  for (int i = 0; i < NUM_THREADS; ++i) {
    rc = thrd_create(&threads[i], (thrd_start_t)incrementCounter, (void *)i);
    if (rc == thrd_error) {
      printf("ERORR; thrd_create() call failed\n");
      exit(EXIT_FAILURE);
    }
  }

  int retval;
  for (int i = 0; i < NUM_THREADS; ++i) {
    thrd_join(threads[i], &retval);
    sum += retval;
  }
  printf("count = %d\n", sum);

  thrd_exit(EXIT_SUCCESS);
}

Ausgabe:

thread 1 started incrementing ID - 140162648991488
thread 0 started incrementing ID - 140162657384192
thread 2 started incrementing ID - 140162640598784
thread 3 started incrementing ID - 140162632206080
count = 40000

Verwenden Sie den Typ thread_local, um eine Variable mit Thread-Speicherdauer zu deklarieren

Alternativ definiert die C-Sprache einen Makroausdruck thread_local, um den Bezeichner als _Thread_local zu bezeichnen. Beachten Sie, dass thread_local-Variablen in einem Dateibereich deklariert werden sollten, damit sie für alle Threads sichtbar sind. Andernfalls kann der Benutzer auch explizit einen static Bezeichner hinzufügen, um seinen Bereich auf Dateiebene zu erweitern. Die Programmstruktur muss geändert werden, da Threads die Werte von thread_local-Objekten an den Hauptthread übermitteln sollen. Der vorherige Beispielcode, der das einfache Zählerprogramm implementiert, sollte geändert werden, um die inkrementierten Zählerwerte an den aufrufenden Thread zurückzugeben. Daher müssen wir die Funktion thrd_join und ihr zweites Argument verwenden, das den Rückgabewert der Thread-Routine speichert. Beachten Sie, dass alle inkrementierten Werte im Hauptthread summiert werden sollten, der auch für den Ausdruck des Endergebnisses im stdout verantwortlich ist.

#include <stdatomic.h>
#include <stdio.h>
#include <stdlib.h>
#include <threads.h>

#ifndef NUM_THREADS
#define NUM_THREADS 4
#endif

thread_local 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;
  }

  return (void *)counter;
}

int main(int argc, char const *argv[]) {
  thrd_t threads[NUM_THREADS];
  int rc, sum = 0;

  for (int i = 0; i < NUM_THREADS; ++i) {
    rc = thrd_create(&threads[i], (thrd_start_t)incrementCounter, (void *)i);
    if (rc == thrd_error) {
      printf("ERORR; thrd_create() call failed\n");
      exit(EXIT_FAILURE);
    }
  }

  int retval;
  for (int i = 0; i < NUM_THREADS; ++i) {
    thrd_join(threads[i], &retval);
    sum += retval;
  }
  printf("count = %d\n", sum);

  thrd_exit(EXIT_SUCCESS);
}

Ausgabe:

thread 1 started incrementing ID - 140162648991488
thread 2 started incrementing ID - 140162640598784
thread 0 started incrementing ID - 140162657384192
thread 3 started incrementing ID - 140162632206080
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

Verwandter Artikel - C Thread