enumerable_thread_specific テンプレート・クラス

概要

スレッド・ローカル・ストレージ用のテンプレート・クラス。

構文

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_specificcombine(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>
        enumerable_thread_specific( Finit finit );
        enumerable_thread_specific( const T& exemplar );
        // C++11 仕様
        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;
    };
}