C ++ 11のthread_local変数は自動的に静的ですか?


85

これら2つのコードセグメントに違いはありますか?

void f() {
    thread_local vector<int> V;
    V.clear();
    ... // use V as a temporary variable
}

そして

void f() {
    static thread_local vector<int> V;
    V.clear();
    ... // use V as a temporary variable
}

裏話:元々、静的ベクトルV(いくつかの中間値を保持するために、関数に入るたびにクリアされます)とシングルスレッドプログラムがありました。プログラムをマルチスレッドプログラムに変えたいので、どういうわけかこの静的修飾子を取り除く必要があります。私の考えは、すべての静的オブジェクトをthread_localに変換し、他に何も心配しないことです。これは裏目に出ることができますか?


16
thread_localローカル変数を持つことは、最初から意味がありません…各スレッドには独自の呼び出しスタックがあります。
Konrad Rudolph

1
いくつかのC関数は元々、静的変数またはグローバル変数のアドレスを返すために作成されました。これは、マルチスレッドアプリ(例:errno、localtime)で使用すると、あいまいなバグにつながることが後で判明しました。さらに、関数が複数のスレッドから呼び出されている場合、または多くの呼び出しオブジェクトとメソッド間でスレッドコンテキストオブジェクトを渡さなければならない場合に、ミューテックスで共有変数を保護することは非常に有害な場合があります。スレッドにローカルな変数は解決しますこれらおよびその他の問題。
edwinc 2015年

3
@Konrad Rudolphは、各スレッドの変数の1つのインスタンスを初期化するのstaticstatic thread_localはなく、ローカル変数を宣言するだけです。
davide 2016年

1
@davideそれは、私にとってもOPにとっても重要ではありません。私たちは話していないstaticstatic thread_localではなく、およそautothread_local事前C ++の11意味使用して、auto(つまり、自動ストレージを)。
コンラッドルドルフ

1
スレッドローカルローカル静的変数を定義する方法も参照してください。簡単な言語弁護士のメモ... MicrosoftとTLSのサポートはVistaを中心に変更されました。スレッドローカルストレージ(TLS)を参照してください。この変更はシングルトンのようなものに影響し、適用される場合とされない場合があります。abondwareソフトウェアモデルを使用している場合は、おそらく問題ありません。複数のコンパイラとプラットフォームをサポートすることに満足している場合は、それに注意を払う必要があるかもしれません。
jww 2016年

回答:


92

C ++標準による

thread_localがブロックスコープの変数に適用されると、明示的に表示されない場合、storage-class-specifierstaticが暗黙的に示されます

つまり、この定義は

void f() {
    thread_local vector<int> V;
    V.clear();
    ... // use V as a temporary variable
}

と同等です

void f() {
    static thread_local vector<int> V;
    V.clear();
    ... // use V as a temporary variable
}

ただし、静的変数はthread_local変数と同じではありません

1 thread_localキーワードで宣言されたすべての変数には、スレッドストレージ期間があります。これらのエンティティのストレージは、それらが作成されたスレッドの期間中持続するものとします。スレッドごとに個別のオブジェクトまたは参照があり、宣言された名前の使用は、現在のスレッドに関連付けられているエンティティを参照します

これらの変数を区別するために、標準では静的ストレージ期間とともに新しい用語のスレッドストレージ期間が導入されています。


1
staticそしてexternこれも、内側のスコープでthread_localの変数のストレージクラスだけリンケージを意味するものではありません。
deduplicator 2014

4
@Deduplicatorブロックスコープ変数にはリンクがありません。だからあなたの履歴書は間違っています。私が投稿に書いたように、それらにはスレッドの保存期間があります。実際には静的ストレージ期間と同じですが、各スレッドに適用されます。
モスクワからのVlad

1
externを追加すると、定義ではなく宣言を行うことになります。そう?
deduplicator 2014

1
@Deduplicatorつまり、ブロックスコープ変数の定義にはリンクがないということです。
モスクワからのVlad

1
VS 2013で試してみたところ、「TL変数を動的に初期化できません」と叫びました。困惑しています。
v.oddou 2016年

19

はい、「スレッドローカルストレージ」は「グローバル」(または「静的ストレージ」)と非常によく似ていますが、「プログラム全体の期間」の代わりに「スレッド全体の期間」があるという点だけが異なります。したがって、ブロックローカルスレッドローカル変数は、コントロールが最初に宣言を通過するときに初期化されますが、各スレッド内で個別に初期化され、スレッドが終了すると破棄されます。


6

と一緒thread_localに使用すると、staticブロックスコープ(@Vladの回答を参照)に含まれ、クラスメンバーに必要です。名前空間スコープのリンケージを意味すると思います。

9.2 / 6による:

クラス定義内では、静的として宣言されていない限り、メンバーはthread_localstorage-class-specifierで宣言されてはなりません。

元の質問に答えるには:

C ++ 11のthread_local変数は自動的に静的ですか?

名前空間スコープ変数を除いて、選択の余地はありません。

これら2つのコードセグメントに違いはありますか?

番号。


4

スレッドローカルストレージは静的ですが、単純な静的ストレージとはまったく異なる動作をします。

変数staticを宣言すると、変数のインスタンスは1つだけになります。コンパイラ/ランタイムシステムは、正確な時期を指定せずに、実際に使用する前にいつか初期化されることを保証します(ここでは一部の詳細は省略されています)。

C ++ 11は、この初期化がスレッドセーフであることを保証しますが、C ++ 11以前は、このスレッドセーフは保証されていませんでした。例えば

static X * pointer = new X;

複数のスレッドが同時に静的初期化コードにヒットした場合、Xのインスタンスがリークする可能性があります。

変数スレッドローカルを宣言すると、変数のインスタンスが多数存在する可能性があります。それらは、thread-idによってインデックス付けされたマップ内にあると考えることができます。つまり、各スレッドは変数の独自のコピーを参照します。

この場合も、変数が初期化されると、コンパイラ/ランタイムシステムは、データが使用される前にこの初期化が行われ、変数を使用するスレッドごとに初期化が行われることを保証します。コンパイラーは、開始がスレッドセーフであることも保証します。

スレッドセーフの保証は、変数を期待どおりに動作させるための舞台裏のコードがかなり存在する可能性があることを意味します。特に、コンパイラにはスレッドの正確な数を事前に知る方法がないことを考慮してください。プログラムに存在し、これらのうちいくつがスレッドローカル変数に影響を与えるか。


@Etherealone:興味深い。どの特定の情報?参考にできますか?
デイルウィルソン

1
stackoverflow.com/a/8102145/1576556。ウィキペディアのC ++ 11の記事では、私が正しく覚えていればそれについて言及しています。
Etherealone 2014

1
ただし、静的オブジェクトは最初に初期化され、次にコピーが割り当てられます。したがって、スレッドセーフな初期化に完全な式が含まれているかどうかも少しわかりません。それ以外の場合はスレッドセーフとは見なされないため、おそらくそうなります。
Etherealone 2014
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.