並列キュークラス

concurrent_queue<T,Alloc> テンプレート・クラスは、T 型値の並列キューを実装します。複数のスレッドがキューから要素を同時にプッシュおよびポップします。キューは無制限で、ブロック操作はありません。キューの基本的な操作は、pushtry_pop です。push 操作は、std::queuepush のように動作します。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> テンプレート・クラスは、ブロック操作を追加して大きさを指定するバリエーションです。特に注目すべきメソッドを次に示します。

concurrent_queue::size() の値は、開始したプッシュ操作の数から、開始したポップ操作の数を引いた値として定義されます。ポップの数がプッシュの数より多い場合、size() は負になります。例えば、concurrent_queue が空で保留中のポップ操作が n ある場合、size()-n を返します。この方法を使用すると、生産タスクは、キュー上で待機している消費タスクの数を簡単に知ることができます。empty() メソッドは、size() が正でない場合にのみ true に定義されます。

デフォルトでは、concurrent_bounded_queue は無制限です。メモリーがなくなるまで、任意の数の値を保持します。この数は、set_capacity メソッドでキューの大きさを設定して制限できます。大きさを設定すると、キューに空間ができるまで push はブロックされます。制限のあるキューは無制限のキューよりも遅いため、キューの大きさを監視するようなコードがプログラム中にある場合は、大きさを設定しないほうが良いでしょう。制限やポップのブロックが必要ない場合は、代わりに concurrent_queue を使用することを検討してください。