C++ 11のthread_local
の説明と混同されています。私の理解では、各スレッドには関数内のローカル変数の一意のコピーがあります。グローバル/静的変数は、すべてのスレッドからアクセスできます(ロックを使用した同期アクセスの可能性があります)。 thread_local
変数はすべてのスレッドに表示されますが、それらが定義されているスレッドによってのみ変更できますか?それが正しいか?
スレッドローカルストレージ期間は、一見グローバルまたは静的なストレージ期間(データを使用する機能の観点から)であるデータを指すために使用される用語ですが、実際には、スレッドごとに1つのコピーがあります。
現在の自動(ブロック/関数中に存在)、静的(プログラム期間中に存在)、動的(割り当てと割り当て解除の間のヒープ上に存在)に追加されます。
スレッドローカルなものは、スレッドの作成時に存在し、スレッドが停止すると破棄されます。
次に例を示します。
シードをスレッドごとに維持する必要がある乱数ジェネレーターを考えてください。スレッドローカルシードを使用すると、各スレッドは他のスレッドとは無関係に、独自の乱数シーケンスを取得します。
シードがランダム関数内のローカル変数である場合、呼び出すたびに初期化され、毎回同じ番号が与えられます。グローバルである場合、スレッドは互いのシーケンスに干渉します。
別の例は、strtok
のようなもので、トークン化状態がスレッド固有ベースで保存されます。このようにして、単一のスレッドは、strtok
への複数の呼び出しにわたって状態を維持しながら、他のスレッドがトークン化の努力を台無しにしないことを確認できます。これは基本的にstrtok_r
安全なバージョン)冗長。
これらの両方の例では、スレッドローカル変数を使用する関数内にwithinが存在することが許可されています。事前にスレッド化されたコードでは、関数内の静的なストレージ期間変数になります。スレッドの場合、ローカルストレージの継続時間をスレッド化するように変更されます。
さらに別の例は、errno
のようなものです。呼び出しの1つが失敗した後、変数をチェックする前にerrno
を変更する個別のスレッドは必要ありませんが、スレッドごとに1つのコピーのみが必要です。
このサイト には、さまざまなストレージ期間指定子の合理的な説明があります。
スレッドローカルストレージは、静的(=グローバル)ストレージのようなあらゆる側面において、各スレッドがオブジェクトの個別のコピーを持っていることのみが特徴です。オブジェクトの存続時間は、スレッドの開始時(グローバル変数の場合)または最初の初期化(ブロックローカルスタティックの場合)のいずれかで始まり、スレッドが終了すると(つまり、join()
が呼び出されると)終了します。
その結果、static
としても宣言できる変数のみがthread_local
として宣言できます。つまり、グローバル変数(より正確には、「名前空間スコープ」の変数)、静的クラスメンバー、ブロック静的変数(その場合はstatic
が暗示されます)。
例として、スレッドプールがあり、作業負荷がどの程度バランスが取れているかを知りたいとします。
thread_local Counter c;
void do_work()
{
c.increment();
// ...
}
int main()
{
std::thread t(do_work); // your thread-pool would go here
t.join();
}
これにより、スレッド使用統計が出力されます。このような実装で:
struct Counter
{
unsigned int c = 0;
void increment() { ++c; }
~Counter()
{
std::cout << "Thread #" << std::this_thread::id() << " was called "
<< c << " times" << std::endl;
}
};