スレッド・ローカル・ストレージ用のテンプレート・クラス。
enum ets_key_usage_type { ets_key_per_instance, ets_no_key }; template <typename T, typename Allocator=cache_aligned_allocator<T>, ets_key_usage_type ETS_key_type=ets_no_key> class enumerable_thread_specific;
#include "tbb/enumerable_thread_specific.h"
enumerable_thread_specific は、T 型の要素にスレッド・ローカル・ストレージ (TLS) を提供します。enumerable_thread_specific は、すべてのスレッドローカル要素のイテレーターと範囲を提供することにより、コンテナーとして動作します。
スレッドローカル要素は遅れて作成されます。新しく作成された enumerable_thread_specific には要素がありません。スレッドが enumerable_thread_specific へのアクセスを要求すると、そのスレッドに対応する要素が作成されます。要素の数は、アプリケーションで使用するスレッドの数ではなく、enumerable_thread_specific にアクセスする (個別の) スレッドの数と同じです。enumerable_thread_specific を消去すると、要素がすべて削除されます。
ETS_key_usage_type 引数は、ネイティブの TLS キーを消費しない実装と、より高いパフォーマンスを提供するが enumerable_thread_specific のインスタンスごとに 1 つのネイティブな TLS キーを消費する特別な実装の選択に使用します。ETS_key_usage_type 引数が提供されない場合、ets_no_key がデフォルトで使用されます。
ネイティブ TLS の数は非常に小さな数 (例えば、64 または 128) に制限されます。このため、最もパフォーマンス・クリティカルなケースにのみ ets_key_per_instance の特別な実装の使用を制限することを推奨します。
enumerable_thread_specific は、tbb::this_tbb_thread::get_id() が返す OS 固有の値を使用してスレッドを識別します。この値は、スレッドの寿命を除いて一意であることは保証されません。新しく作成されたスレッドには、すでに破棄されたスレッドと同じ OS 固有の ID が割り当てられます。このため、enumerable_thread_specific の要素数は local() を呼び出した実際の (個別の) スレッド数よりも少ないことがあります。また、スレッドによる最初の参照で enumerable_thread_specific に返された要素は新しく構築されないことがあります。
次のコードは、enumerable_thread_specific を使用する単純な例を示しています。null_parallel_for_body::operator() の呼び出しの数および実行される反復数の合計は、parallel_for に参加する各スレッドによってカウントされます。これらのカウントはメインの最後で出力されます。
#include <cstdio> #include <utility> #include "tbb/task_scheduler_init.h" #include "tbb/enumerable_thread_specific.h" #include "tbb/parallel_for.h" #include "tbb/blocked_range.h" typedef tbb::enumerable_thread_specific< std::pair<int,int> > CounterType; CounterType MyCounters (std::make_pair(0,0)); struct Body { void operator()(const tbb::blocked_range<int> &r) const { CounterType::reference my_counter = MyCounters.local(); ++my_counter.first; for (int i = r.begin(); i != r.end(); ++i) ++my_counter.second; } }; int main() { tbb::parallel_for( tbb::blocked_range<int>(0, 100000000), Body()); for (CounterType::const_iterator i = MyCounters.begin(); i != MyCounters.end(); ++i) { printf("Thread stats:\n"); printf(" calls to operator(): %d", i->first); printf(" total # of iterations executed: %d\n\n", i->second); } }
enumerable_thread_specific の combine(f) メソッドは、ラムダ式を使用して記述できるバイナリー・ファンクター f を使用してリダクションを行います。例えば、前のサンプルは、main 関数の最後に次の行を追加することにより、スレッドローカル値を合計するように拡張できます。
std::pair<int,int> sum = MyCounters.combine([](std::pair<int,int> x, std::pair<int,int> y) { return std::make_pair(x.first+y.first, x.second+y.second); }); printf("Total calls to operator() = %d, " "total iterations = %d\n", sum.first, sum.second);
namespace tbb { template <typename T, typename Allocator=cache_aligned_allocator<T>, ets_key_usage_type ETS_key_type=ets_no_key > class enumerable_thread_specific { public: // 基本型 typedef Allocator allocator_type; typedef T value_type; typedef T& reference; typedef const T& const_reference; typedef T* pointer; typedef implementation-dependent size_type; typedef implementation-dependent difference_type; // イテレーター型 typedef implementation-dependent iterator; typedef implementation-dependent const_iterator; // 並列範囲型 typedef implementation-dependent range_type; typedef implementation-dependent const_range_type; // 構築および破棄 enumerable_thread_specific(); template <typename Finit> explicit enumerable_thread_specific( Finit finit ); explicit enumerable_thread_specific( const T& exemplar ); // Supported since C++11 explicit enumerable_thread_specific( T&& exemplar ); template <typename... Args> enumerable_thread_specific( Args&&... args ); ~enumerable_thread_specific(); // コピー・コンストラクターと代入演算子 enumerable_thread_specific( const enumerable_thread_specific& other ); template<typename Alloc, ets_key_usage_type Cachetype> enumerable_thread_specific( const enumerable_thread_specific<T, Alloc, Cachetype>& other ); enumerable_thread_specific& operator=( const enumerable_thread_specific& other ); template<typename Alloc, ets_key_usage_type Cachetype> enumerable_thread_specific& operator=( const enumerable_thread_specific<T, Alloc, Cachetype>& other ); // C++11 からサポートされた move コンストラクターと代入演算子 enumerable_thread_specific( enumerable_thread_specific&& other ); template<typename Alloc, ets_key_usage_type Cachetype> enumerable_thread_specific( enumerable_thread_specific<T, Alloc, Cachetype>&& other ); enumerable_thread_specific& operator=( enumerable_thread_specific&& other ); template<typename Alloc, ets_key_usage_type Cachetype> enumerable_thread_specific& operator=( enumerable_thread_specific<T, Alloc, Cachetype>&& other ); // その他のコンテナー全体の操作 void clear(); // 並列操作 reference local(); reference local( bool& exists ); size_type size() const; bool empty() const; // 結合 template<typename FCombine> T combine( FCombine fcombine ); template<typename Func> void combine_each( Func f ); // 並列反復 range_type range( size_t grainsize=1 ); const_range_type range( size_t grainsize=1 ) const; // イテレーター iterator begin(); iterator end(); const_iterator begin() const; const_iterator end() const; }; }