web-dev-qa-db-ja.com

循環バッファーのスレッドセーフ実装

BoostライブラリのCircular_bufferはスレッドセーフではありません。そのため、以下に示すように、boost :: circular_bufferオブジェクトをクラスでラップしました。スレッド間の相互排除は、条件変数、ミューテックス、ロックの取得/解放を使用することで実現されます(私はそう思います)。この実装スレッドは安全ですか?

#include <boost/thread/condition.hpp>
#include <boost/thread/mutex.hpp>
#include <boost/thread/thread.hpp>
#include <boost/circular_buffer.hpp>

// Thread safe circular buffer 
template <typename T>
class circ_buffer : private boost::noncopyable
{
public:
    typedef boost::mutex::scoped_lock lock;
    circ_buffer() {}
    circ_buffer(int n) {cb.set_capacity(n);}
    void send (T imdata) {
        lock lk(monitor);
        cb.Push_back(imdata);
        buffer_not_empty.notify_one();
    }
    T receive() {
        lock lk(monitor);
        while (cb.empty())
            buffer_not_empty.wait(lk);
        T imdata = cb.front();
        cb.pop_front();
        return imdata;
    }
    void clear() {
        lock lk(monitor);
        cb.clear();
    }
    int size() {
        lock lk(monitor);
        return cb.size();
    }
    void set_capacity(int capacity) {
        lock lk(monitor);
        cb.set_capacity(capacity);
    }
private:
    boost::condition buffer_not_empty;
    boost::mutex monitor;
    boost::circular_buffer<T> cb;
};

編集これは、(cv::Matオブジェクトだけでなく)任意のタイプのオブジェクトを受け入れるテンプレートクラスになりました。

32
Alexey

はい。
すべてのパブリックメソッドを同じロックでロックすると、スレッドセーフになります。

読み取り/書き込みロックの使用を検討できます。これは、同時読み取りが多い場合にパフォーマンスが向上する可能性があります。

読者が少ない場合はオーバーヘッドが増えるだけですが、オプションを確認してテストする価値があります。

16
Yochai Timmer

sendで作成されたMatの無意味なコピーがいくつかあることを除いて、私はそれがうまく見えると思います。新しいものは必要ありません。sendの引数を直接cbにプッシュできます。

5

あなたの実装は、この blogger で示されているものと似ています。そのブログを読んで、実装で何かを見落としていないか確認してください。

Matオブジェクトの作成/コピーにコストがかかる場合は、継続的に作成/コピー/削除することは避けてください。代わりに、ある種のパイプラインアーキテクチャでrecycledを継続的に取得するMatオブジェクトのプール(別名フリーリスト)が必要です。このタイプのアーキテクチャについては、関連する質問のこの answer で説明します。

その答えでは、ブロッキングスタックを使用してプールを実装することをお勧めしましたが、ブロッキングcircular_buffer。スタックを提案した理由は、キャッシュフレンドリーである可能性があると思ったからですが、実際にそれが違いを生むかどうかを測定することはありませんでした。

2
Emile Cormier

buffer_not_full条件をまったく使用していないことを除いて、一見すると見栄えがします。おそらく、buffer_not_emptyコードに類似したコードを追加する必要があります。