How to Use thread_local Variables in C

Jinku Hu Feb 02, 2024
  1. Use the _Thread_local Type to Declare Variable With Thread Storage Duration
  2. Use the thread_local Type to Declare Variable With Thread Storage Duration
How to Use thread_local Variables in C

This article will explain several methods of how to use thread_local variables in C.

Use the _Thread_local Type to Declare Variable With Thread Storage Duration

The C language defines multiple keywords for different storage classes like auto, static, register, extern. Since the specification of the C11 standard, the _Thread_local specifier was added. _Thread_local storage duration starts at the moment of the thread creation and ends with its termination. The value stored in the _Thread_local object is initialized when the thread is started and cleaned up when the thread terminates. In general, the thread-local objects are another alternative to avoid race conditions in shared resources. Namely, we implicitly separate the data between threads as _Thread_local qualified objects have separate instances for each thread.

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

Output:

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

Use the thread_local Type to Declare Variable With Thread Storage Duration

Alternatively, the C language defines a macro expression thread_local to denote the specifier as _Thread_local. Note that thread_local variables should be declared in a file scope to be visible to all threads, or the user may explicitly add a static specifier as well to expand its scope to file level. The program structure will need to be modified as threads should communicate the values of thread_local objects to the main thread. The previous example code that implements the simple counter program should be modified to pass the incremented counter values back to the calling thread. Thus, we need to utilize the thrd_join function and its second argument that stores the thread routine’s return value. Mind that, all incremented values should be summed in the main thread, which is also responsible for printing out the final result to the stdout.

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

Output:

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

Related Article - C Thread