concurrent_queue<T,Alloc> テンプレート・クラスは、T 型値の並列キューを実装します。複数のスレッドがキューから要素を同時にプッシュおよびポップします。キューは無制限で、ブロック操作はありません。キューの基本的な操作は、push と try_pop です。push 操作は、std::queue の push のように動作します。try_pop 操作は、利用可能なアイテムをポップします。スレッド安全性のために、1 つの操作でチェックとポップを行わなければなりません。
例えば、次のシリアルコードについて考えてみます。
extern std::queue<T> MySerialQueue; T item; if( !MySerialQueue.empty() ) { item = MySerialQueue.front(); MySerialQueue.pop_front(); ... process item... }
std::queue メソッドがそれぞれスレッドセーフな方法で実装されても、ほかのスレッドも同じキューからポップしている場合、サンプルで示されているようなメソッドの構成はスレッドセーフになりません。例えば、別のスレッドが MySerialQueue から最後のアイテムを取得する直前に MySerialQueue.empty() が true を返すことがあります。
等価のスレッドセーフなインテル® スレッディング・ビルディング・ブロック (インテル® TBB) コードは次のようになります。
extern concurrent_queue<T> MyQueue; T item; if( MyQueue.try_pop(item) ) { ...process item... }
シングルスレッド・プログラムでは、キューは FIFO (先入れ先出し) 構造です。しかし、マルチスレッド・プログラムの場合、プッシュとポップが同時に行われるため、「先」の定義は不確定です。concurrent_queue は、スレッドが 2 つの値をプッシュして、別のスレッドがその 2 つの値をポップする場合に、プッシュされたのと同じ順で値がポップされることを保証します。
concurrent_queue テンプレート・クラスは無制限で、待機するメソッドがありません。オーバーフローを回避するために同期を提供する、またはキューが空でなくなるのを待つのは、ユーザーの責任です。一般に、より高いレベルで同期を行う必要がある場合は、このテンプレート・クラスが適切です。
concurrent_bounded_queue<T,Alloc> テンプレート・クラスは、ブロック操作を追加して大きさを指定するバリエーションです。特に注目すべきメソッドを次に示します。
pop(item) は、成功するまで待機します。
push(item) は、キューの大きさを超えずに成功するまで待機します。
try_push(item) は、キューの大きさを超えない場合のみ item をプッシュします。
size() は、符号付き 整数を返します。
concurrent_queue::size() の値は、開始したプッシュ操作の数から、開始したポップ操作の数を引いた値として定義されます。ポップの数がプッシュの数より多い場合、size() は負になります。例えば、concurrent_queue が空で保留中のポップ操作が n ある場合、size() は -n を返します。この方法を使用すると、生産タスクは、キュー上で待機している消費タスクの数を簡単に知ることができます。empty() メソッドは、size() が正でない場合にのみ true に定義されます。
デフォルトでは、concurrent_bounded_queue は無制限です。メモリーがなくなるまで、任意の数の値を保持します。この数は、set_capacity メソッドでキューの大きさを設定して制限できます。大きさを設定すると、キューに空間ができるまで push はブロックされます。制限のあるキューは無制限のキューよりも遅いため、キューの大きさを監視するようなコードがプログラム中にある場合は、大きさを設定しないほうが良いでしょう。制限やポップのブロックが必要ない場合は、代わりに concurrent_queue を使用することを検討してください。