Я играю с std :: атомными структурами и написал эту многопользовательскую многопользовательскую очередь без блокировки, которую я здесь прикрепляю. Идея очереди состоит из двух стеков - стека производителей и потребителей, которые по существу связаны структурами списков. Узлы списков содержат индексы в массиве, в котором хранятся фактические данные, где вы будете читать или писать.Атомные операции многоточечной очереди
Идея состоит в том, что узлы для списков являются взаимоисключающими, то есть указатель на узел может существовать только в списке производителей или потребителей. Производитель попытается получить узел из списка производителей, потребитель из списка потребителей и всякий раз, когда указатель на узел будет получен либо производителем, либо потребителем, он должен быть из обоих списков, чтобы никто другой не смог его приобрести. Я использую функции std :: atomic_compare_exchange для вращения до тех пор, пока не выскочит узел.
Проблема заключается в том, что с логикой должно быть что-то не так, или операции не являются атомарными, поскольку я предполагаю, что это происходит потому, что даже с 1 производителем и 1 потребителем, учитывая достаточно времени, очередь будет жить и что я заметил заключается в том, что если вы утверждаете, что ячейка! = cell-> m_next, утверждение будет поражено! Так что его, вероятно, что-то, глядя мне в лицо, и я просто не вижу, поэтому мне интересно, если кто-то может браться.
Thx
#ifndef MTQueue_h
#define MTQueue_h
#include <atomic>
template<typename Data, uint64_t queueSize>
class MTQueue
{
public:
MTQueue() : m_produceHead(0), m_consumeHead(0)
{
for(int i=0; i<queueSize-1; ++i)
{
m_nodes[i].m_idx = i;
m_nodes[i].m_next = &m_nodes[i+1];
}
m_nodes[queueSize-1].m_idx = queueSize - 1;
m_nodes[queueSize-1].m_next = NULL;
m_produceHead = m_nodes;
m_consumeHead = NULL;
}
struct CellNode
{
uint64_t m_idx;
CellNode* m_next;
};
bool push(const Data& data)
{
if(m_produceHead == NULL)
return false;
// Pop the producer list.
CellNode* cell = m_produceHead;
while(!std::atomic_compare_exchange_strong(&m_produceHead,
&cell, cell->m_next))
{
cell = m_produceHead;
if(!cell)
return false;
}
// At this point cell should point to a node that is not in any of the lists
m_data[cell->m_idx] = data;
// Push that node as the new head of the consumer list
cell->m_next = m_consumeHead;
while (!std::atomic_compare_exchange_strong(&m_consumeHead,
&cell->m_next, cell))
{
cell->m_next = m_consumeHead;
}
return true;
}
bool pop(Data& data)
{
if(m_consumeHead == NULL)
return false;
// Pop the consumer list
CellNode* cell = m_consumeHead;
while(!std::atomic_compare_exchange_strong(&m_consumeHead,
&cell, cell->m_next))
{
cell = m_consumeHead;
if(!cell)
return false;
}
// At this point cell should point to a node that is not in any of the lists
data = m_data[cell->m_idx];
// Push that node as the new head of the producer list
cell->m_next = m_produceHead;
while(!std::atomic_compare_exchange_strong(&m_produceHead,
&cell->m_next, cell))
{
cell->m_next = m_produceHead;
}
return true;
};
private:
Data m_data[queueSize];
// The nodes for the two lists
CellNode m_nodes[queueSize];
volatile std::atomic<CellNode*> m_produceHead;
volatile std::atomic<CellNode*> m_consumeHead;
};
#endif
Это выглядит как вопрос ABA мне: https://stackoverflow.com/questions/25628225/where-is-the-race/25628314#25628314 – dohashi
Может быть ... Как бы один тест для этого? – bitwise
ABA довольно сложно проверить. Я бы предложил действительно понять проблему ABA, а затем продумать ваш алгоритм, чтобы убедиться, что он уязвим. Модификация вашей структуры данных как конечного автомата также может помочь. Атомные структуры данных могут быть очень сложными для правильной работы. – dohashi