web-dev-qa-db-ja.com

C ++ 11のthread_localとはどういう意味ですか?

C++ 11のthread_localの説明と混同されています。私の理解では、各スレッドには関数内のローカル変数の一意のコピーがあります。グローバル/静的変数は、すべてのスレッドからアクセスできます(ロックを使用した同期アクセスの可能性があります)。 thread_local変数はすべてのスレッドに表示されますが、それらが定義されているスレッドによってのみ変更できますか?それが正しいか?

102
polapts

スレッドローカルストレージ期間は、一見グローバルまたは静的なストレージ期間(データを使用する機能の観点から)であるデータを指すために使用される用語ですが、実際には、スレッドごとに1つのコピーがあります。

現在の自動(ブロック/関数中に存在)、静的(プログラム期間中に存在)、動的(割り当てと割り当て解除の間のヒープ上に存在)に追加されます。

スレッドローカルなものは、スレッドの作成時に存在し、スレッドが停止すると破棄されます。

次に例を示します。

シードをスレッドごとに維持する必要がある乱数ジェネレーターを考えてください。スレッドローカルシードを使用すると、各スレッドは他のスレッドとは無関係に、独自の乱数シーケンスを取得します。

シードがランダム関数内のローカル変数である場合、呼び出すたびに初期化され、毎回同じ番号が与えられます。グローバルである場合、スレッドは互いのシーケンスに干渉します。

別の例は、strtokのようなもので、トークン化状態がスレッド固有ベースで保存されます。このようにして、単一のスレッドは、strtokへの複数の呼び出しにわたって状態を維持しながら、他のスレッドがトークン化の努力を台無しにしないことを確認できます。これは基本的にstrtok_r安全なバージョン)冗長。

これらの両方の例では、スレッドローカル変数を使用する関数内にwithinが存在することが許可されています。事前にスレッド化されたコードでは、関数内の静的なストレージ期間変数になります。スレッドの場合、ローカルストレージの継続時間をスレッド化するように変更されます。

さらに別の例は、errnoのようなものです。呼び出しの1つが失敗した後、変数をチェックする前にerrnoを変更する個別のスレッドは必要ありませんが、スレッドごとに1つのコピーのみが必要です。

このサイト には、さまざまなストレージ期間指定子の合理的な説明があります。

119
paxdiablo

スレッドローカルストレージは、静的(=グローバル)ストレージのようなあらゆる側面において、各スレッドがオブジェクトの個別のコピーを持っていることのみが特徴です。オブジェクトの存続時間は、スレッドの開始時(グローバル変数の場合)または最初の初期化(ブロックローカルスタティックの場合)のいずれかで始まり、スレッドが終了すると(つまり、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;
     }
};
20
Kerrek SB