C++11以降のC++はModern C++と言われ、それ以前のC++(古典的C++)に対して多くの言語仕様が追加された。古典的C++しか知らないプログラマがModern C++の言語仕様を使い倒したコードを見ても、さっぱり分からないかもしれない。
古典的C++を主戦場とするプログラマに対して、盲目的にModern C++に移行すべきだと言うつもりはない。ただ、Modern C++は古典的C++の上位互換であり、古典的C++の枠組みで書かれたコードに対してModern C++の一部の仕様をつまみ食い的に利用するだけでも、おいしいことはある。
そこで、古典的C++の枠組みでコードを書くプログラマがつまみ食いする価値のあるModern C++の仕様について、いくつかの記事に分けて紹介していく。
スレッドローカル変数
スレッドローカル変数は、スレッド内部でのみ値が共有される変数である。個人的には、「スレッド内グローバル変数」と呼んだ方が実態に合っている気がする。マルチスレッドのプログラムでは、スレッドローカル変数のようなスコープの変数が使えると便利なこともあるだろう。
古典的C++プログラマにとっては、マルチスレッドのプログラムではpthreadを用いることが多いと思うが、スレッドローカル変数を用いるにはModern C++でのstd::threadを用いる必要がある。
下記は、スレッドローカル変数およびstd::threadを用いたプログラム例である。変数Xはスレッド内ローカル変数であり、他のスレッドがXの値を変更しても自身のスレッドには影響しない。
#include <stdio.h>
#include <unistd.h>
#include <thread>
thread_local int X = 0;
void set_x(int a) {
X = a;
}
int get_x() {
return X;
}
void thread_func(int id) {
set_x(id);
printf("Before sleep, Thread ID = %d, X = %d\n", id, get_x());
sleep(1);
printf("After sleep, Thread ID = %d, X = %d\n", id, get_x());
}
int main() {
std::thread t1(thread_func, 1); sleep(0);
std::thread t2(thread_func, 2); sleep(0);
std::thread t3(thread_func, 3);
t1.join();
t2.join();
t3.join();
}
上記プログラムの実行結果の例は、以下のようになる。
変数Xはスレッドローカル変数なので、スレッドごとに別々の値が保持され、スレッドをまたいで干渉されることはない。
$ ./a.out
Before sleep, Thread ID = 1, X = 1
Before sleep, Thread ID = 2, X = 2
Before sleep, Thread ID = 3, X = 3
After sleep, Thread ID = 3, X = 3
After sleep, Thread ID = 2, X = 2
After sleep, Thread ID = 1, X = 1