6

グローバル変数を使用する再入不可関数があるとします。


int i;
void foo(void){
/* modify i */
}

そして、この関数をマルチスレッド コードで使用したいので、次のようにコードを変更できます。


void foo(int i){
/* modify i */
}

または、gcc __thread 指定子を使用すると、より簡単になります。


__thread int i;
void foo(void){
/* modify i */
}

最後の利点は、foo() を呼び出す別のコードを変更する必要がないことです。

私の質問は、スレッド ローカル ストレージのオーバーヘッドはどれくらいですか? TLS には明らかではない問題がありますか?

次のように、別のポインターを介して TLS`ed 変数を変更する場合、オーバーヘッドはありますか?


__thread int i;
void foo(void){
int *p = &i;
/* modify i using p pointer */
}

ありがとう。

4

2 に答える 2

10

そして、この関数をマルチスレッド コードで使用したいので、次のようにコードを変更できます。

void foo(int i){
    /* modify i */
}

のコピーを変更するだけなので、これは確かに機能しませんi。変更を保持したい場合は、代わりにint*orを渡す必要があります。int&

TLS を使用しても、同じ機能を実装するために従う可能性のあるカスタム アプローチよりも (空間または時間のいずれかで) 大きなオーバーヘッドが発生することはありません。実際のコンパイラは、スレッドローカル変数を保持するグローバルデータ構造にストレージ「スロット」を動的に割り当てることにより、TLS を実装します。

実行時にスレッド ローカル変数にアクセスする場合、間接的なレベルが追加されます。最初に、ランタイムは現在のスレッドの適切なスレッド ローカル変数テーブルにアクセスし、次にテーブルから値をフェッチする必要があります。このフェッチは、配列へのインデックスを使用して行われます (これは O(1) 操作です)。

これを行う場合:

__thread int i;
void foo(void){
    int *p = &i;
    /* modify i using p pointer */
}

iその場合、ポインターを使用してアクセスする必要はありません。i実行中のスレッドごとに異なる値を持つグローバル変数と考えてください。変更を保持するためにポインターを介して通常のグローバルにアクセスする必要はないため、スレッドローカル変数でポインターを使用する必要もありません。

最後に、スレッド ローカル ストレージは、実際にはスレッドごとに多数の変数を格納することを意図したものではありません (TLS テーブルのサイズにはコンパイラに依存する制限があります) が、これは簡単に回避できるものですstructstructスレッドローカルへのポインタ。

于 2011-03-27T16:56:35.347 に答える
1

TLS に関して私が目にする唯一の問題は、サイズが制限される可能性があることです。システムに依存するため、移植やスケーリングの問題に直面する可能性があります (ところで、一部のシステムでは TLS がまったく利用できない場合があります)。

于 2011-03-27T17:01:48.460 に答える