Usar variables thread_local en C

Jinku Hu 12 octubre 2023
  1. Utilice el tipo _Thread_local para declarar variable con duración de almacenamiento de subprocesos
  2. Utilice el tipo thread_local para declarar variable con duración de almacenamiento de subprocesos
Usar variables thread_local en C

Este artículo explicará varios métodos de cómo utilizar variables thread_local en C.

Utilice el tipo _Thread_local para declarar variable con duración de almacenamiento de subprocesos

El lenguaje C define múltiples palabras clave para diferentes clases de almacenamiento como auto, static, register, extern. Desde la especificación del estándar C11, se agregó el especificador _Thread_local. La duración del almacenamiento de _Thread_local comienza en el momento de la creación del hilo y finaliza con su terminación. El valor almacenado en el objeto _Thread_local se inicializa cuando se inicia el hilo y se limpia cuando termina el hilo. En general, los objetos locales de subprocesos son otra alternativa para evitar condiciones de carrera en recursos compartidos. Es decir, separamos implícitamente los datos entre subprocesos ya que los objetos calificados _Thread_local tienen instancias separadas para cada subproceso.

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

Producción :

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

Utilice el tipo thread_local para declarar variable con duración de almacenamiento de subprocesos

Alternativamente, el lenguaje C define una expresión macro thread_local para denotar el especificador como _Thread_local. Tenga en cuenta que las variables thread_local deben declararse en un alcance de archivo para que sean visibles para todos los subprocesos, o el usuario puede agregar explícitamente un especificador static también para expandir su alcance al nivel de archivo. La estructura del programa deberá modificarse, ya que los subprocesos deben comunicar los valores de los objetos thread_local al subproceso principal. El código de ejemplo anterior que implementa el programa de contador simple debe modificarse para pasar los valores de contador incrementados de nuevo al hilo de llamada. Por lo tanto, necesitamos utilizar la función thrd_join y su segundo argumento que almacena el valor de retorno de la rutina del hilo. Tenga en cuenta que todos los valores incrementados deben sumarse en el hilo principal, que también es responsable de imprimir el resultado final en el 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);
}

Producción :

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

Artículo relacionado - C Thread