Readers Writerロックで、並行キューを実装した。
値を返し、例外を投げないpop()
も実装しておいた。
実装: shand/concurrent/queue.hpp
インタフェース
namespace shand { template <class T> // Tは、例外を投げないムーブコンストラクタを持っていること class concurrent_queue { public: concurrent_queue() {} concurrent_queue(const concurrent_queue&) = delete; concurrent_queue& operator=(const concurrent_queue&) = delete; // write access void push(const T& x); void push(T&& x); boost::optional<T> pop() noexcept; void clear(); // read access std::size_t size() const; bool empty() const; }; } // namespace shand
サンプルコード
#include <iostream> #include <thread> #include <shand/concurrent/queue.hpp> void producer(shand::concurrent_queue<int>& que) { for (int i = 0; i < 100; ++i) { que.push(i); } } void consumer(shand::concurrent_queue<int>& que) { int i = 0; for (;;) { if (boost::optional<int> x = que.pop()) { std::cout << x.get() << std::endl; ++i; } if (i > 30) // てきとうなところで終了する return; } } int main() { shand::concurrent_queue<int> que; std::thread t1(producer, std::ref(que)); std::thread t2(consumer, std::ref(que)); t1.join(); t2.join(); }
出力:
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
値を返し、例外を投げないpop()の実装
値を返し、例外を投げないpop()
は、以下のようにして実装しています。
- 戻り値は、コピーではなくムーブで返す
- 空の場合を考慮して、
optional
で返す - クラスのテンプレートパラメータは、例外を投げないムーブコンストラクタを持っている型のみを受け付ける
optional
のムーブコンストラクタは、T
のムーブが例外を投げなければ、例外を投げない(と、1.57.0のドキュメントに書いていた)
テンプレートパラメータの要件部分:
template <class T> class concurrent_queue { static_assert( std::is_nothrow_move_constructible<T>::value, "Type T must be nothrow move constructible"); … };
pop()
の実装部分:
// que_はstd::deque<T> boost::optional<T> pop() noexcept { std::lock_guard<std::shared_timed_mutex> lock(mutex_); if (que_.empty()) return boost::none; T front = std::move(que_.front()); que_.pop_front(); return front; }