コンパイラーは未使用の静的なthread_localクラスメンバーを無視しましたか?


10

クラスでスレッド登録をしたいので、thread_local機能のチェックを追加することにします。

#include <iostream>
#include <thread>

class Foo {
 public:
  Foo() {
    std::cout << "Foo()" << std::endl;
  }
  ~Foo() {
    std::cout << "~Foo()" << std::endl;
  }
};

class Bar {
 public:
  Bar() {
    std::cout << "Bar()" << std::endl;
    //foo;
  }
  ~Bar() {
    std::cout << "~Bar()" << std::endl;
  }
 private:
  static thread_local Foo foo;
};

thread_local Foo Bar::foo;

void worker() {
  {
    std::cout << "enter block" << std::endl;
    Bar bar1;
    Bar bar2;
    std::cout << "exit block" << std::endl;
  }
}

int main() {
  std::thread t1(worker);
  std::thread t2(worker);
  t1.join();
  t2.join();
  std::cout << "thread died" << std::endl;
}

コードは簡単です。私のBarクラスには静的thread_localメンバーがありますfoo。static thread_local Foo fooが作成された場合は、スレッドが作成されたことを意味します。

しかし、コードを実行しても何もFoo()出力されずBar、を使用するのコンストラクターでコメントを削除するfooと、コードは正常に機能します。

GCC(7.4.0)とClang(6.0.0)で試してみましたが、結果は同じです。コンパイラfooが未使用でインスタンスを生成しないことを発見したと思います。そう

  1. コンパイラーはstatic thread_localメンバーを無視しましたか?これをデバッグするにはどうすればよいですか?
  2. もしそうなら、なぜ通常のstaticメンバーがこの問題を抱えていないのですか?

回答:


9

観察に問題はありません。 [basic.stc.static] / 2は、静的ストレージ期間を持つ変数の削除を禁止します。

静的ストレージ期間の変数に初期化または副作用のあるデストラクタがある場合、[class.copy]で指定されているようにクラスオブジェクトまたはそのコピー/移動が削除される場合を除いて、未使用のように見えても削除されません。 。

この制限は、他のストレージ期間には存在しません。実際、[basic.stc.thread] / 2はこう言っています:

スレッドストレージ期間を持つ変数は、最初のodr-useの前に初期化され構築されている場合、スレッドの終了時に破棄されます。

これは、odr-usedを使用しない限り、スレッドストレージ期間を持つ変数を構築する必要がないことを示唆しています。


しかし、なぜこの矛盾が生じるのでしょうか?

静的ストレージ期間の場合、プログラムごとに変数のインスタンスは1つだけです。その構築の副作用は重大な場合があり(プログラム全体のコンストラクターのようなもの)、そのため副作用が必要です。

ただし、スレッドローカルストレージ期間については、問題があります。アルゴリズムが多くのスレッドを開始する場合があります。これらのスレッドのほとんどでは、変数は完全に無関係です。呼び出しを行う外部の物理シミュレーションライブラリがstd::reduce(std::execution::par_unseq, first, last)大量のfooインスタンスを作成することになるとしたら、それは陽気なことでしょう。

もちろん、odrで使用されないスレッドローカルストレージ期間の変数の構築の副作用(たとえば、スレッドトラッカー)の合法的な使用が可能です。ただし、これを保証する利点は、前述の欠点を補うのに十分ではないため、これらの変数は、odrを使用しない限り削除できます。(ただし、コンパイラは実行しないことを選択できます。また、これを処理する独自のラッパーを作成することもできますstd::thread。)


1
いいでしょう...標準がそう言っているのなら、そうではありません...しかし、委員会が副作用を静的ストレージ期間と見なさないのは奇妙ではありませんか?
ravenisadesk

@reavenisadesk更新された回答を参照してください。
LF

1

この情報は「スレッドローカルストレージのELF処理」にあり、@ LFの答えを証明できます。

さらに、ランタイムサポートは、必要がない場合はスレッドローカルストレージの作成を回避する必要があります。たとえば、ロードされたモジュールは、プロセスを構成する多数のスレッドの1つでのみ使用される場合があります。すべてのスレッドにストレージを割り当てるのは、メモリと時間の無駄です。遅延メソッドが必要です。動的にロードされたオブジェクトを処理する必要があるため、まだ割り当てられていないストレージを認識する必要があるため、これはそれほど大きな負担にはなりません。これは、すべてのスレッドを停止し、すべてのスレッドを再実行する前にすべてのスレッドにストレージを割り当てる唯一の方法です。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.