task_arena クラスと this_task_arena 名前空間

概要

明示的なユーザー管理タスク・スケジューラー領域を表現するクラス、および現在の領域に適用される関数の名前空間。

構文

class task_arena;

ヘッダー

#include "tbb/task_arena.h"

説明

task_arena クラスは、タスクを共有して実行する場所を表します。

task_arena のタスクを同時に実行するスレッド数は並列レベルにより制限されます。並列レベルはその領域にのみ影響し、以前の task_scheduler_init 仕様の影響を受けません。

スケジューラーが使用するスレッド数の合計は、次のいずれか大きいほうにより制限されます: マシンのデフォルトのスレッド数または最初のタスク・スケジューラーの初期化で指定された値。そのため、task_arena に割り当てられるスレッド数は、並列レベルに関係なく、最大値を超えることはありません。さらに、指定されたスレッド数が許容最大値より小さい場合でも、task_arena に指定されたスレッド数が割り当てられないことがあります。

タスク・スケジューラーを明示的または暗黙的に初期化する各ユーザースレッドは、暗黙の内部タスク領域オブジェクトを作成して使用します。1 つの領域にスポーンまたは追加されたタスクは、ほかの領域では実行できません。

task_arena のインスタンスは、内部表現への参照を保持しますが、その存続期間を完全には管理しません。内部表現は、タスクが含まれるか、ほかのスレッドが参照している間は破棄できません。

task_arena コンストラクターは、内部領域オブジェクトを作成しません。コンストラクターをアタッチするときにすでに存在していない場合、明示的に task_arena::initialize を呼び出して作成されるか、初回使用時に遅れて作成されます。

インテル® スレッディング・ビルディング・ブロック (インテル® TBB) は、task_arena で表された明示的な領域オブジェクトを処理する場合、現在のスレッドに対して暗黙の領域オブジェクトを割り当てません。しかし、同じスレッドの後続のスケジューラー初期化では、task_scheduler_init に指定されたスレッド数に関係なくデフォルトの設定が使用されます。これは、タスク・スケジューラーに関連するメソッドの呼び出しが、暗黙の領域オブジェクトの作成を含め、現在のスレッドのスケジューラーを初期化していた、以前のバージョンのインテル® TBB との互換性を提供します。

this_task_arena 名前空間は、呼び出しスレッドで現在使用されている領域 (明示的な task_arena または暗黙的な領域オブジェクトのいずれか) と対話するグローバル関数を含みます。

メンバー

namespace tbb {
    class task_arena {
    public:
        static const int automatic = implementation-defined;
        static const int not_initialized = implementation-defined;
        struct attach {};

        task_arena(int max_concurrency = automatic, unsigned reserved_for_masters = 1);
        task_arena(const task_arena &s);
        explicit task_arena(task_arena::attach);
        ~task_arena();

        void initialize();
        void initialize(int max_concurrency, unsigned reserved_for_masters = 1);
        void initialize(task_arena::attach);
        void terminate();

        bool is_active() const;
        int max_concurrency() const;

        // C++11 よりも前でサポート
        template<typename F> void execute(F& f);
        template<typename F> void execute(const F& f);
        template<typename F> void enqueue(const F& f);
        template<typename F> void enqueue(const F& f, priority_t p);

        // C++11 以降でサポート
        template<typename F> auto execute(F& f) -> decltype(f());
        template<typename F> auto execute(const F& f) -> decltype(f());
        template<typename F> void enqueue(F&& f);
        template<typename F> void enqueue(F&& f, priority_t p);

        static int current_thread_index(); // 非推奨
    };
    namespace this_task_arena {
        int current_thread_index();
        int max_concurrency();

        // C++11 よりも前でサポート
        template<typename F> void isolate(F& f);
        template<typename F> void isolate(const F& f);

        // C++11 以降でサポート
        template<typename F> auto isolate(F& f) -> decltype(f());
        template<typename F> auto isolate(const F& f) -> decltype(f());
    }
}

サンプル

次のサンプルは、同時に 2 つの paralel_for ループを実行します。1 つはスケーラブルで、もう 1 つはスケーラブルではありません。スケーラブルでないループは最大 2 スレッドに制限されるため、ほとんどのスレッドはスケーラブルなループに使用できます。特定のタスクのサブセットを待機するには、task_group を使用します。

tbb::task_scheduler_init def_init; // デフォルトのスレッド数を使用
tbb::task_arena limited(2);        // この領域のスレッド数を 2 に制限
tbb::task_group tg;

limited.execute([&]{ // このジョブで最大 2 つのスレッドを使用
    tg.run([]{ // タスクグループで実行
        tbb::parallel_for(1, N, unscalable_work());
    });
});

// 上記のループと同時に別のジョブを実行
// 別のジョブはデフォルトのスレッド数まで使用できる
tbb::parallel_for(1, M, scalable_work());

// 制限された領域でタスクグループの完了を待つ
limited.execute([&]{ tg.wait(); });
次の表は、task_arena クラスのメンバーの詳細な情報を提供します。
メンバー 説明
task_arena(int max_concurrency = automatic, unsigned reserved_for_masters = 1)

特定の同時実行数の制限 (max_concurrency) で task_arena を作成します。制限の一部は reserved_for_masters のアプリケーション・スレッド向けに予約できます。予約の量は制限を超えることはできません。

インテル® TBB 4.4 Update 1 以前は、reserved_for_masters の有効な値は 0 および 1 のみでした。

注意

max_concurrency および reserved_for_masters が 1 よりも大きな等しい値に明示的に設定された場合、インテル® TBB のワーカースレッドは領域をジョインしません。そのため、これらの領域ではキューに入れられたタスクの実行は保証されません。ワーカースレッドなしに設定された領域で task_arena::enqueue() および task::enqueue() を使用しないでください。

static const int automatic

max_concurrency として上記のコンストラクターに渡されると、ハードウェア構成に基づいて領域の同時実行数が自動的に設定されます。

static const int not_initialized

メソッドまたは関数により返された場合、アクティブな領域がない、または領域オブジェクトがまだ初期化されていないことを示します。

task_arena(const task_arena&)

別の task_arena インスタンスから設定をコピーします。

explicit task_arena(task_arena::attach)

呼び出しスレッドで現在使用されている内部領域に接続される task_arena のインスタンスを作成します。領域がまだ存在しない場合、デフォルト引数で task_arena を作成します。

ほかのコンストラクターとは異なり、すでに存在する領域に接続したとき新しい task_arena を自動的に初期化します。
~task_arena()

内部領域表現への参照を削除し、task_arena() のインスタンスを破棄します。ほかのメソッドを同時に呼び出した場合、スレッドセーフではありません。

void initialize()

void initialize(int max_concurrency, unsigned reserved_for_masters = 1)

void initialize(task_arena::attach)

内部領域表現の初期化を実行します。引数が指定されている場合、以前の領域パラメーターをオーバーライドします。task_arena::attach クラスのインスタンスが引数として指定され、呼び出しスレッドで現在使用されている内部領域が存在する場合、メソッドは領域パラメーターを無視して task_arena をその内部領域に接続します。すでに初期化されている領域に対して呼び出された場合、効果はありません。

initialize の呼び出しの後、領域のパラメーターは固定され、変更することはできません。
void terminate()

task_arena オブジェクトを破棄しないで内部領域表現への参照を削除します。task_arena オブジェクトは再利用できます。ほかのメソッドを同時に呼び出した場合、スレッドセーフではありません。

bool is_active() const

タスク領域が初期化された場合は true を返し、その他の場合は false を返します。

int max_concurrency() const

領域の並列レベルを返します。領域を初期化する必要はなく、初期化を行いません。

C++11 よりも前:template<F> void enqueue(const F& f)

C++11 以降:template<F> void enqueue(F&& f)

指定されたファンクター (タスクへコピーまたは移動される) を処理するため、タスクを領域に追加し、直ちにリターンします。

呼び出しスレッドは、領域をジョインする必要はありません。つまり、領域外の任意の数のスレッドは、ブロックすることなくワークを領域に追加できます。

注意

領域に追加されたタスクが、ほかのタスクと同時に実行される保証はありません。

注意

ファンクターでスローされた例外がキャッチされない場合の動作は不定です。

C++11 よりも前:template<F> void enqueue(const F& f, priority_t)

C++11 以降:template<F> void enqueue(F&& f, priority_t)

指定された優先度でタスクを追加します。その他は enqueue(f) に似ています。

C++11 よりも前:

template<F> void execute(F&)

template<F> void execute(const F&)

C++11 以降:

template<F> auto execute(F&) -> decltype(f())

template<F> auto execute(const F&) -> decltype(f())

指定されたファンクターを領域で実行します。C++11 以降では、関数はファンクターの戻り値を返します。

呼び出しスレッドは、可能であれば領域をジョインして、ファンクターを実行します。リターン時に、以前のタスク・スケジューラー状態と浮動小数点設定を復元します。

領域をジョインできない場合は、ファンクターをタスクにラップして領域に追加し、OS カーネルの同期オブジェクトを使用してジョインする機会を待ち、タスクの完了後に終了します。

注意

領域 (B) を (B.execute([]{ A.execute([]{ B.execute(f); }); }); のように) 間接的に 2 回ジョインすると、この領域の有効な同時実行数が減り、デッドロックを引き起こすことがあります。

領域外の任意の数のスレッドがワークを追加し、待機することができます。ただし、ワークを実行できるのは、領域で指定されている最大スレッド数までです。

execute により、ワーカースレッドに対する領域の要求が減り、ワーカーが領域を離れ、その結果呼び出しスレッドが実行できるようになる場合があります。

ファンクターでスローされた例外はキャプチャーされ、execute から再スローされます。正しい例外の伝播が利用できないか無効な場合、例外は (同じスレッドの例外でも) tbb::captured_exception にラップされます。
static int current_thread_index()

古い機能 (非推奨)。呼び出しスレッドがまだタスク・スケジューラーを初期化していない場合に -1 を返すことを除いて this_task_arena::current_thread_index() (下記参照) と同じです。

次の表は、task_arena 名前空間のメンバーの情報を提供します。
メンバー 説明
int current_thread_index()

呼び出しスレッドで現在使用されているタスク領域のスレッド・インデックスを返します。スレッドがまだタスク・スケジューラーを初期化していない場合は task_arena::not_initialized を返します。

スレッド・インデックスは、0 から領域の並列レベルまでの整数です。スレッド・インデックスは、領域にジョインしているときにアプリケーション (マスター) スレッドとワーカースレッドの両方に割り当てられ、領域を離れるまで保持されます。領域を共有するスレッドのインデックスは一意です (つまり、領域内の 2 つのスレッドが同じインデックスを持つことはありません) が、必ずしも連続していません。

タスクを実行しない場合、スレッドはいつでも領域を離れることがあるため、スレッドのインデックスは、同じタスクグループやアルゴリズムに属している 2 つのタスク間でも変わることがあります。

異なる領域を使用するスレッドは、同じ現在のインデックス値を持つことがあります。

execute() に入れ子の領域をジョインすると、リターン時に戻される外側の領域のインデックスが保存され、現在のインデックス値が変更されることがあります。

この方法は、例えば、領域に入ろうとしているスレッドを特定のハードウェアに関連付けるために、task_scheduler_observer のプレビュー機能によって使用されます。
int max_concurrency()

呼び出しスレッドで現在使用されているタスク領域の並列レベルを返します。スレッドがまだタスク・スケジューラーを初期化していない場合、ハードウェア構成により自動的に決定された並列レベルを返します。

C++11 よりも前:

template<F> void isolate(F&)

template<F> void isolate(const F&)

C++11 以降:

template<F> auto isolate(F&) -> decltype(f())

template<F> auto isolate(const F&) -> decltype(f())

指定されたファンクターの有効範囲 (分離領域とも呼ばれます) でスケジュールされたタスクのみ処理するように呼び出しスレッドを制限して、ユーザー定義ファンクターを分離して実行します。C++11 以降では、関数はファンクターの戻り値を返します。

注意

ファンクターによって返されるオブジェクトは参照であってはなりません。代わりに、std::reference_wrapper を使用することができます。

注意

フローグラフや task_group のような非同期並列構造は、分離された領域内で注意して使用する必要があります。分離して実行される graph::wait_for_alltask_group::wait では、フローグラフのノードで try_put を呼び出して (または task_group::run で) スケジュールされたタスクは、同じ分離領域 (またはその領域で以前にスポーンされたタスク) でスケジュールされた場合にのみアクセスできます。その他の場合にアクセスすると、パフォーマンス問題やデッドロックが発生します。

関連情報