Для реализации циклической очереди с одним производителем / потребителем блокировки фактически не требуются.Просто установите условие, при котором производитель не может записывать в очередь, если очередь заполнена, а потребитель не может читать из очереди, если она пуста.Также производитель всегда будет писать в указатель tail
, который указывает на первый доступный пустой слот в очереди, а потребитель будет читать из указателя head
, который представляет первый непрочитанный слот в очереди.
Ваш код может выглядеть как в следующем примере кода (примечание: я предполагаю, что в инициализированной очереди tail == head
и оба указателя объявлены volatile
, чтобы оптимизирующий компилятор не переупорядочивал последовательность операцийв данном потоке. На x86 не требуется никаких барьеров памяти из-за сильной модели согласованности памяти для архитектуры, но это изменилось бы на других архитектурах с более слабыми моделями согласованности памяти, где требовались бы барьеры памяти):
queue_type::pointer queue_type::next_slot(queue_type::pointer ptr);
bool queue_type::enqueue(const my_type& obj)
{
if (next_slot(tail) == head)
return false;
*tail = obj;
tail = next_slot(tail);
return true;
}
bool queue_type::dequeue(my_type& obj)
{
if (head == tail)
return false;
obj = *head;
head = next_slot(head);
return true;
}
Функция next_slot
просто увеличивает указатель head
или tail
, чтобы она возвращала указатель на следующий слот в массиве и учитывала любые функции обхода массива.
Наконец, мы гарантируем синхронизацию в модели одного производителя / потребителя, потому что мы не увеличиваем указатель tail
, пока он не записывает данные в слот, на который он указывал, и мы не увеличиваем head
указатель, пока мы не прочитаем данные из слота, на который он указывал.Поэтому вызов dequeue
не вернется действительным, пока не будет выполнен хотя бы один вызов enequeue
, а указатель tail
никогда не перезапишет указатель head
из-за регистрации в enqueue
.Кроме того, только один поток увеличивает указатель tail
, а один поток увеличивает указатель head
, поэтому не возникает проблем с общим чтением или записью из одного и того же указателя, которые могут создать проблемы синхронизации, требующие блокировки илинекоторый тип атомарной операции.