Use variáveis thread_local em C

Jinku Hu 12 outubro 2023
  1. Use o tipo _Thread_local para declarar variável com duração de armazenamento de thread
  2. Use o tipo thread_local para declarar variável com duração de armazenamento de thread
Use variáveis thread_local em C

Este artigo irá explicar vários métodos de como usar variáveis ​​thread_local em C.

Use o tipo _Thread_local para declarar variável com duração de armazenamento de thread

A linguagem C define várias palavras-chave para diferentes classes de armazenamento como auto, static, register, extern. Desde a especificação do padrão C11, o especificador _Thread_local foi adicionado. A duração do armazenamento _Thread_local começa no momento da criação da thread e termina com o seu término. O valor armazenado no objeto _Thread_local é inicializado quando o encadeamento é iniciado e limpo quando o encadeamento termina. Em geral, os objetos de segmento local são outra alternativa para evitar condições de corrida em recursos compartilhados. Ou seja, separamos implicitamente os dados entre os encadeamentos, pois os objetos qualificados _Thread_local têm instâncias separadas para cada encadeamento.

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

Resultado:

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 o tipo thread_local para declarar variável com duração de armazenamento de thread

Alternativamente, a linguagem C define uma expressão macro thread_local para denotar o especificador como _Thread_local. Observe que as variáveis ​​thread_local devem ser declaradas em um escopo de arquivo para serem visíveis a todos os encadeamentos, ou o usuário pode incluir explicitamente um especificador static também para expandir seu escopo para o nível de arquivo. A estrutura do programa precisará ser modificada, pois as threads devem comunicar os valores dos objetos thread_local para a thread principal. O código de exemplo anterior que implementa o programa de contador simples deve ser modificado para passar os valores de contador incrementados de volta ao encadeamento de chamada. Portanto, precisamos utilizar a função thrd_join e seu segundo argumento que armazena o valor de retorno da rotina de thread. Lembre-se de que todos os valores incrementados devem ser somados na thread principal, que também é responsável por imprimir o resultado final no 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);
}

Resultado:

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

Artigo relacionado - C Thread