C 言語で thread_local 変数を使用する

胡金庫 2023年10月12日
  1. スレッドストレージ期間で _Thread_local タイプを使用して変数を宣言する
  2. スレッドストレージ期間で thread_local タイプを使用して変数を宣言する
C 言語で thread_local 変数を使用する

この記事では、C 言語で thread_local 変数を使用する方法のいくつかの方法について説明します。

スレッドストレージ期間で _Thread_local タイプを使用して変数を宣言する

C 言語は、autostaticregisterextern などのさまざまなストレージクラスに複数のキーワードを定義します。C11 標準の仕様以降、_Thread_local 指定子が追加されました。_Thread_local の保存期間は、スレッドの作成時に始まり、スレッドの終了で終わります。_Thread_local オブジェクトに格納されている値は、スレッドの開始時に初期化され、スレッドの終了時にクリーンアップされます。一般に、スレッドローカルオブジェクトは、共有リソースの競合状態を回避するためのもう 1つの方法です。つまり、_Thread_local 修飾オブジェクトはスレッドごとに個別のインスタンスを持っているため、スレッド間でデータを暗黙的に分離します。

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

出力:

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

スレッドストレージ期間で thread_local タイプを使用して変数を宣言する

あるいは、C 言語はマクロ式 thread_local を定義して、指定子を _Thread_local として示します。thread_local 変数は、すべてのスレッドに表示されるようにファイルスコープで宣言する必要があることに注意してください。そうでない場合、ユーザーは明示的に static 指定子を追加してスコープをファイルレベルに拡張できます。スレッドは thread_local オブジェクトの値をメインスレッドに伝達する必要があるため、プログラム構造を変更する必要があります。単純なカウンタープログラムを実装する前のサンプルコードは、インクリメントされたカウンター値を呼び出し元のスレッドに返すように変更する必要があります。したがって、thrd_join 関数と、スレッドルーチンの戻り値を格納する 2 番目の引数を利用する必要があります。増分されたすべての値はメインスレッドで合計する必要があります。メインスレッドは、最終結果を 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);
}

出力:

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
著者: 胡金庫
胡金庫 avatar 胡金庫 avatar

DelftStack.comの創設者です。Jinku はロボティクスと自動車産業で8年以上働いています。自動テスト、リモートサーバーからのデータ収集、耐久テストからのレポート作成が必要となったとき、彼はコーディングスキルを磨きました。彼は電気/電子工学のバックグラウンドを持っていますが、組み込みエレクトロニクス、組み込みプログラミング、フロントエンド/バックエンドプログラミングへの関心を広げています。

LinkedIn Facebook

関連記事 - C Thread