Когда я читаю атомарную рассылку о примере реализации кольцевого буфера без ожидания:
Мне интересно, если memory_order_acquire необходимо в
if (next_head == tail_.load(boost::memory_order_acquire))
Кажется, что memory_order_relaxed должен работать так же. Мой аргумент в том, что
value = ring_[tail];
случается, перед тем
tail_.store(next(tail), boost::memory_order_release)
в вызове pop (). поэтому мы уверены, что данные были прочитаны перед сохранением в вызове push () как
ring_[head] = value;
Я вставил весь приведенный ниже пример кода для удобства.
Спасибо!
#include <boost/atomic.hpp>
template<typename T, size_t Size>
class ringbuffer {
public:
ringbuffer() : head_(0), tail_(0) {}
bool push(const T & value)
{
size_t head = head_.load(boost::memory_order_relaxed);
size_t next_head = next(head);
if (next_head == tail_.load(boost::memory_order_acquire))
// Может ли tail_.load выше использовать boost :: memory_order_relaxed?
return false;
ring_[head] = value;
head_.store(next_head, boost::memory_order_release);
return true;
}
bool pop(T & value)
{
size_t tail = tail_.load(boost::memory_order_relaxed);
if (tail == head_.load(boost::memory_order_acquire))
return false;
value = ring_[tail];
tail_.store(next(tail), boost::memory_order_release);
return true;
}
private:
size_t next(size_t current)
{
return (current + 1) % Size;
}
T ring_[Size];
boost::atomic<size_t> head_, tail_;
};
Одна причина в том, что по порядку:
if(next_head == tail_.load(boost::memory_order_acquire))
return false;
ring_[head] = value; // A non-atomic store.
memory_order_acquire
гарантирует, что следующее неатомарное хранилище не будет переупорядочено, чтобы предшествовать этой загрузке tail_
,
memory_order_relaxed
с другой стороны, не препятствует переупорядочению, и, следовательно, не является достаточным здесь.
(Предполагается, что boost::memory_order
эквивалентно std::memory_order
.)
В строго упорядоченных системах — x86, SPARC TSO, мэйнфрейме IBM и т. Д. — упорядочение по типу выпуска-приобретения является автоматическим для большинства операций. Никаких дополнительных инструкций ЦП для этого режима синхронизации не выдается; затрагиваются только некоторые оптимизации компилятора (например, компилятору запрещается перемещать неатомарные хранилища после выпуска атомарного хранилища или выполнять неатомарные загрузки раньше, чем получение атомарной нагрузки). В слабо упорядоченных системах (ARM, Itanium, PowerPC) используются специальные инструкции по загрузке процессора или ограничению памяти.
Насколько я вижу, оба tail_.load(boost::memory_order_acquire)
в push()
а также head_.load(boost::memory_order_acquire)
в pop()
можно расслабить и заменить xx.load(boost::memory_order_relaxed)
,