Реализация потокового буферного буфера
Circular_buffer из библиотеки boost не является потокобезопасным. Поэтому я завернул boost:: circle_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
).
Ответы
Ответ 1
Да.
Если вы заблокируете все общедоступные методы с помощью той же блокировки, это будет потокобезопасно.
Вы можете использовать блокировки чтения-записи, которые могут иметь лучшую производительность, если у вас много параллельных читателей.
Если у вас мало читателей, это просто добавит накладные расходы, но может стоить проверить опцию и тестирование.
Ответ 2
Я думаю, что это выглядит хорошо, за исключением того, что есть несколько бессмысленных копий Mat, сделанных в send
. Вам не нужно новое, вы можете напрямую нажать аргумент send
на свой cb.
Ответ 3
Ваша реализация аналогична реализации, описанной в этом blogger. Вы должны прочитать этот блог, чтобы узнать, пропустили ли вы что-либо в своей реализации.
Если ваши объекты Mat
дороги для создания/копирования, вам следует избегать постоянного создания/копирования/удаления. Вместо этого у вас должен быть пул (aka free list) объектов Mat, который постоянно получает recycled в какой-то конвейерной архитектуре. Я описываю этот тип архитектуры в этом ответе на соответствующий вопрос.
В этом ответе я предложил использовать блокирующий стек для реализации пула, но вы также можете использовать блокировку circular_buffer
. Причина, по которой я предложил стек, состояла в том, что я думал, что это может быть более безопасным для кэша, но я никогда не пытался понять, будет ли это иметь значение.
Ответ 4
Выглядит хорошо на первый взгляд, за исключением того, что вы вообще не используете условие buffer_not_full
. Вероятно, вы захотите добавить код, похожий на код buffer_not_empty
.