Я занимаюсь многопоточным программированием на С++, и мне интересно, существует ли поточно-безопасная реализация кольцевого буфера на С++, или у вас есть идеи, как я могу это реализовать.
С++ реализация кольцевого буфера с поддержкой потоков
Ответы (1)
Вот базовая реализация. Требует, чтобы объекты, хранящиеся в буфере, были конструируемыми по умолчанию и копируемыми (сохраняя их в std::vector‹>). Требуется поддержка C++11
(для std::atomic
). Почти любая последняя версия gcc
будет иметь его с -std=c++11
или -std=c++0x
.
Если c++11
недоступен, замените соответствующий встроенный компонент компилятора, чтобы сделать head_
и tail_
атомарными.
Должен быть безопасным для одного потока чтения и одного потока записи.
Опубликуйте элементы, позвонив:
auto val = ringbuffer.back();
val = some_value;
ringbuffer.push();
Получите товар по телефону:
auto val = ringbuffer.front();
// do stuff with val
ringbuffer.pop();
Если back()
возвращает nullptr
, то буфер "полный". Если front()
возвращает nullptr
, то буфер "пуст".
Внимание, не тестировалось (вообще) :D
#include <vector>
template <class T>
class RingBuffer
{
public:
RingBuffer(size_t buffer_size)
: ring_(buffer_size)
, buffer_size_(buffer_size)
, head_(0)
, tail_(0)
{
}
T* back()
{
bool received = false;
if(available(head_, tail_))
{
return &(ring_[head_ % buffer_size_]);
}
return nullptr;
}
void push()
{
++head_;
}
T* front()
{
if(tail_ < head_)
{
return & ring_[tail_ % buffer_size_];
}
return nullptr;
}
void pop()
{
++tail_;
}
size_t size() const
{
if(tail_ < head_)
return buffer_size_ - ((tail_ + buffer_size_) - head_);
else if(tail_ > head_)
return buffer_size_ - (tail_ - head_);
return 0;
}
bool available()
{
return available(head_, tail_);
}
private:
bool available(uint64_t h, uint64_t t) const
{
if(h == t)
return true;
else if(t > h)
return (t - h) > buffer_size_;
else// if(h > t)
return (t + buffer_size_) - h > 0;
}
std::vector<T> ring_;
const size_t buffer_size_;
std::atomic<uint64_t> head_;
std::atomic<uint64_t> tail_;
};
person
Chad
schedule
27.09.2013
Я не понял, что вы имеете в виду, если С++ 11 недоступен, замените соответствующий встроенный компилятор для того, чтобы сделать head_ и tail_ атомарными.
- person Avb Avb; 28.09.2013
head_
и tail_
должны быть атомарными, а это означает, что изменение (или чтение их значений) должно быть атомарной операцией. Если вы не понимаете, что это значит, то я думаю, что вы, вероятно, не готовы к многопоточному программированию.
- person Chad; 28.09.2013
Хм. Не уверен, что понял. Например,
size
не уязвим для вызова push
из другого потока и изменения значения head_
между if
и return
?
- person Tom; 31.03.2015
Размер правильный на момент входа (в эту функцию) из одного потока. Два последующих вызова size могут возвращать разные значения. Хотя это не проверено.
- person Chad; 31.03.2015
Я не думаю, что это вообще потокобезопасно, так как вы можете вызвать front() или back() в любой момент времени, повесить указатель и изменить базовые данные. Чтобы сделать его потокобезопасным, я думаю, вам нужно либо предоставить push(somedata)/pop и обернуть доступ к ring_ во мьютексе внутри, либо обернуть весь доступ к этому классу во мьютексе извне. Я не эксперт, но я не думаю, что в настоящее время атомщики действительно делают что-то для защиты самих данных.
- person pilkch; 22.12.2015
C++11
? - person Chad   schedule 28.09.2013