__thread キーワード
C++11 以前に処理系が実装していたスレッドローカルストレージ (以下 TLS) のためのキーワード. 動的初期化は行えないため, 静的変数に付与して使用する. MSVC なら __declspec(thread).
疑問
スレッドが終了した時に __thread を使用して確保された領域はどうなるのか? 開放されずにメモリリークになったりするのか?
検証
tls_sample.cpp
#include <iostream>
#include <set>
#include <thread>
static std::set<int*> tls_set__;
static __thread int tls__[(1 << 20) / sizeof(int)];
struct Worker {
int i;
Worker(int i) : i(i) {}
void operator()() {
tls_set__.insert(tls__);
*tls__ = i;
std::this_thread::sleep_for(std::chrono::nanoseconds(1));
}
};
void TLSTest() {
int i_max = 1 << 10, j_max = 256;
std::vector<std::thread> threads(i_max);
for (int i = 0; i < i_max; i += j_max) {
// スレッドを j_max 個作成
for (int j = 0; j < j_max; ++j) {
threads[i + j] = std::thread(Worker(i));
}
for (int j = 0; j < j_max; ++j) {
threads[i + j].join();
}
}
std::cout << tls_set__.size() << std::endl;
}
int main() {
TLSTest();
return 0;
}
/*実行結果
142
*/
作成したスレッド総数より TLS の数が少ないので, 使われなくなった TLS は回収されて使いまわされるように見える.
スレッド関数内の sleep 時間を伸ばすなどして同時に生きているスレッド数を上げると, TLS 数も増加した.
あまりにも大きなサイズの TLS をたくさんのスレッドで確保すれば静的領域を使い切るかもしれない.
静的領域の大きさは固定なはずだが、TLS に使われる領域はどのように用意している?
追検証(VC++で)
tls_sample2.cpp
#include <iostream>
int main() {
#define PRINTP(x) std::cout << #x << " : " << x << "(" << &x << ")" << std::endl;
static int static__[1 << 10] = { 1 };
static __declspec(thread) int tls__[1 << 10] = { 10 };
static int stack[1 << 10] = { 100 };
int* heap = new int[1 << 10]; heap[0] = 1000;
PRINTP(static__[0]);
PRINTP(tls__[0]);
PRINTP(stack[0]);
PRINTP(heap[0]);
return 0;
}
/* 実行結果
static__[0] : 1(00007FF78FF59000)
tls__[0] : 10(0000004D144640E0)
stack[0] : 100(00007FF78FF5A200)
heap[0] : 1000(0000004D1446EA00)
*/
変数のアドレスを見るに, TLS は実はヒープ領域に確保されているようだ. OSX 上で Clang を使って実行しても同様の結果が得られた.