読者です 読者をやめる 読者になる 読者になる

shared_timed_mutexで並行キューを実装した

C++

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;
}

参照