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 との互換性を提供します。

メンバー

namespace tbb {
    class task_arena {
    public:
        static const int automatic = implementation-defined;
        struct attach {};
        static int current_thread_index();
        task_arena(int max_concurrency = automatic, unsigned reserved_for_masters = 1);
        task_arena(const task_arena &s);
        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();
        template<typename F> void enqueue(const F& f);
        template<typename F> void enqueue(const F& f, priority_t p);
        template<typename F> void execute(F& f);
        template<typename F> void execute(const F& 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());

// execute() の内側でタスクグループを
// 待つようにする
// このタスクグループのタスクのみを待つ
limited.execute([&]{ tg.wait(); });

次の表は、このテンプレート・クラスのメンバーの詳細な情報を提供します。

メンバー 説明
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 が明示的に等しく設定された場合、インテル® TBB のワーカースレッドは領域をジョインしません。そのため、これらの領域ではキューに入れられたタスクの実行は保証されません。ワーカースレッドなしに設定された領域で task_arena::enqueue() および task::enqueue() を使用しないでください。

static const int automatic

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

task_arena(const task_arena&)

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

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()

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

template<F> void enqueue(const F&)

指定されたファンクターを処理するため、タスクを領域に追加し、直ちにリターンします。

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

注意

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

注意

ファンクターでスローされた例外がキャッチされない場合の動作は不定です。
template<F> void enqueue(const F&, priority_t)

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

template<F> void execute(F&)

template<F> void execute(const F&)

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

注意

すでに領域内にあるスレッドが execute への入れ子の呼び出しを行うと、領域のキューに入れられたワークを処理できないことがあります。

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

注意

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

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

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

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

戻り値: 呼び出しスレッドで現在使用されているタスク領域のスレッド・インデックス。スレッドがまだタスク・スケジューラーを初期化していない場合は -1。スレッド・インデックスは、0 から領域の並列レベルまでの整数です。スレッド・インデックスは、領域にジョインしている間はアプリケーション (マスター) スレッドとワーカースレッドの両方に割り当てられ、領域を離れるまで保持されます。領域を共有するスレッドのインデックスは一意です (つまり、領域内の 2 つのスレッドが同じインデックスを持つことはありません) が、必ずしも連続していません。

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

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

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

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

関連情報