最終更新日時(UTC):
が更新

履歴 編集

スレッドローカルストレージ [N2659](C++11)

このページはC++11に採用された言語機能の変更を解説しています。

のちのC++規格でさらに変更される場合があるため関連項目を参照してください。

概要

変数宣言の際に、記憶域としてthread_localキーワードを指定することで、スレッドごとの静的記憶域に変数が保持される。

staticキーワードを記憶域として使用した変数は、プログラムを通してひとつの状態を持ち、プログラム終了時に変数が破棄される。thread_localキーワードの場合はスレッドごとに状態を持ち、スレッド終了時に変数が破棄される。

// スレッドごとに、0から始まるIDを生成して返す関数
int create_id()
{
  thread_local int current_id = 0;
  return current_id++;
}

// 2つのスレッドそれぞれで、IDが0から始まる
std::thread t1([]{
  int id1 = create_id(); // id1 == 0
  int id2 = create_id(); // id2 == 1
});

std::thread t2([]{
  int id1 = create_id(); // id1 == 0
  int id2 = create_id(); // id2 == 1
});

t1.join();
t2.join();

仕様

  • thread_localキーワードを記憶域として指定された変数は、「スレッド記憶域の有効期間 (thread storage duration)」を持つ。この記憶域を持つ変数は、スレッドの開始から終了までの有効期間を持つ。
  • thread_localキーワードは、staticexternを除き、registerといった他の記憶域キーワードと同時には使用できない。
  • スレッド終了時には、スレッド記憶域を持つ変数のデストラクタが呼び出される。
  • スレッド記憶域を持つ変数のデストラクタ、もしくは名前空間スコープを持つスレッド記憶域変数のコンストラクタで例外が送出された場合、スレッドを初期化する関数の関数tryブロックでは、その例外を捕捉できない。
  • プログラム終了時の動作は、std::exit()関数とstd::quick_exit()関数のページを参照。

#include <iostream>
#include <thread>
#include <random>

// 範囲[min_inclusive, max_inclusive]でランダム一様分布する整数を生成する。
// スレッドごとに乱数の状態を持つ。
int random_range(int min_inclusive, int max_inclusive)
{
  std::random_device seed_gen;
  thread_local std::mt19937 engine(seed_gen());
  std::uniform_int_distribution<int> dist(min_inclusive, max_inclusive);
  return dist(engine);
}

int main()
{
  // 複数のスレッドから並行にrandom_range()関数を呼び出せる
  std::thread t1([]{
    int random_value = random_range(0, 100);

    // ※coutに対する一度の書き込みはスレッドセーフであるため、3つの書き込みを1つに統合。
    std::cout << "thread1 : " + std::to_string(random_value) + "\n";
  });

  std::thread t2([]{
    int random_value = random_range(0, 100);
    std::cout << "thread2 : " + std::to_string(random_value) + "\n";
  });

  t1.join();
  t2.join();
}

出力例

thread1 : 67
thread2 : 4

この機能が必要になった背景・経緯

マルチスレッドアプリケーションでは、スレッドごとにデータを一意に維持することがたびたび必要となる。これはスレッドローカルストレージと呼ばれ、多くのベンダーがスレッド記憶域の言語拡張を用意していた:

ベンダー 機能
GNU Thread-Local Storage
HP スレッドローカルストレージを指定する記憶クラス指定子が存在した。 例: __declspec(__thread) int x = 0;
IBM Thread-Local Storage in What's New in XL C/C++ V9.0
IBM __thread ストレージ・クラス指定子
Intel GCC互換の__threadキーワードが存在した。
Microsoft __declspec thread
Oracle(旧Sun Microsystems) Thread-Local Storage

各ベンダーのこれらの経験を標準C++に導入することとなった。

関連項目

参照