C 言語で thread_local 変数を使用する
この記事では、C 言語で thread_local
変数を使用する方法のいくつかの方法について説明します。
スレッドストレージ期間で _Thread_local
タイプを使用して変数を宣言する
C 言語は、auto
、static
、register
、extern
などのさまざまなストレージクラスに複数のキーワードを定義します。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