明示的なユーザー管理タスク・スケジューラー領域の表現。
class task_arena;
#include "tbb/task_arena.h"
task_arena クラスは、複数のスレッド (スレッド数は最大並列レベルにより制限される) がタスクを共有し実行する、内部タスク・スケジューラー・オブジェクトを表します。
task_arena の並列レベルは分離されており、以前の task_scheduler_init 仕様の影響を受けません。
タスク・スケジューラーを明示的または暗黙的に初期化する各ユーザースレッドには、暗黙の内部タスク領域オブジェクトが含まれます。タスク領域にスポーンまたは追加されたタスクは、ほかのタスク領域では実行できません。
task_arena のインスタンスは、内部表現への参照を作成して保持しますが、その存続期間を完全には管理しません。内部表現は、タスクが含まれるか、ほかのワーカースレッドが参照している間は破棄できません。
namespace tbb { class task_arena { public: static const int automatic = implementation-defined; static int current_thread_index(); task_arena(int max_concurrency = automatic, unsigned reserved_for_masters = 1); task_arena(const task_arena &s); ~task_arena(); void initialize(); void initialize(int max_concurrency, unsigned reserved_for_masters = 1); 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) |
特定の並列レベルで、一部がアプリケーション・スレッド向けに予約された task_arena を作成します。 注インテル® TBB 4.3 の task_arena の実装において、reserved_for_masters の有効な値は 0 と 1 のみです。 |
static const int automatic |
max_concurrency として上記のコンストラクターに渡されると、ハードウェア構成に基づいてタスク領域の並列性が自動で設定されます。 |
task_arena(const task_arena&) |
別の task_arena インスタンスから設定をコピーします。 |
~task_arena() |
内部タスク領域表現への参照を削除し、task_arena() のインスタンスを破棄します。ほかのメソッドを同時に呼び出した場合、スレッドセーフではありません。 |
void initialize();
void initialize(int max_concurrency, unsigned reserved_for_masters = 1); |
内部タスク領域表現の初期化を実行します。引数が指定されている場合、以前の並列化の設定をオーバーライドします。すでに初期化されているタスク領域に対して呼び出された場合、効果はありません。注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 のプレビュー機能によって使用されます。 |