Buduję dość skomplikowaną aplikację w Qt, która może dynamicznie ładować dynamiczne biblioteki i uruchamiać je jako wątki, i muszą przekazywać informacje między sobą tak szybko, jak to możliwe, więc pomyślałem, że kolejka atomowa być mój najlepszy scenariusz, więc jest to plik AtomicQueue.hpp:Implementacja kolejki atomowej w QT5
#ifndef ATOMICQUEUE_HPP
#define ATOMICQUEUE_HPP
#include <QAtomicPointer>
// Used http://www.drdobbs.com/parallel/writing-lock-free-code-a-corrected-queue/210604448?pgno=2
// as reference
template<class T>
class AtomicQueue
{
struct QueueNode
{
QueueNode(const T& value) : next(NULL), data(value) {}
~QueueNode() { if (next) delete next; }
QueueNode *next;
T data;
};
public:
AtomicQueue()
{
m_front = new QueueNode(T());
m_tail.store(m_front);
m_divider.store(m_front);
}
~AtomicQueue() {}
void push(const T& value)
{
m_tail.load()->next = new QueueNode(value);
m_tail = m_tail.load()->next; // This moves the QueueNode into the atomic pointer, making it safe :)
while(m_front != m_divider.load())
{
QueueNode *tmp = m_front;
m_front = m_front->next;
delete tmp;
}
}
bool peek(T& result)
{
if (m_divider.load() != m_tail.load())
{
// Problem area
QueueNode *next = m_divider.load()->next;
if (next)
{
result = next->data;
return true;
}
}
return false;
}
bool pop(T& result)
{
bool res = this->peek(result);
if (res)
{
m_divider = m_divider.load()->next;
}
return res;
}
private:
QueueNode *m_front;
QAtomicPointer<QueueNode> m_divider, m_tail;
};
#endif // ATOMICQUEUE_HPP
przerwy kolejki po i pchania i pop jeden przedmiot, a ja nie mogę zrozumieć dlaczego. Jestem całkiem nowy w programowaniu wątków w stylu Atomic, więc całkiem możliwe, że po prostu nie rozumiem konkretnego aspektu tego. Podczas uruchamiania w trybie debugowania otrzymuję błąd segregacji SEGSEV w mojej funkcji bool AtomicQueue::peek
, gdy wykonuję result = next->data
.
Czy ktoś może wskazać, co robię źle?
Aktualizacja:
Więc rozwiązaniu problemu, który był w moim QueueNode destructor. Zasadniczo, kiedy usunięty węzeł, to próbować oczyścić wszystkie kolejne węzły, które następnie dał mi kilka różnych tras do mocowania go:
- Ustaw flagę w QueueNode, który mówi, że nie, aby usunąć wszystkie
->next
węzłów - Odłącz węzeł z przodu przed usunięciem go
- Pozostawić AtomicQueue zarządzać oczyszczanie węzłów
Zdecydowałem się na trzeciej opcji, ponieważ już zrobiłem jakieś porządki podczas pchania nowe węzły, więc tutaj jest ustalona klasa dla wszystkich zainteresowanych:
#ifndef ATOMICQUEUE_HPP
#define ATOMICQUEUE_HPP
#include <QAtomicPointer>
// Used http://www.drdobbs.com/parallel/writing-lock-free-code-a-corrected-queue/210604448?pgno=2
// as reference
template<class T>
class AtomicQueue
{
struct QueueNode
{
QueueNode(const T& value) : next(NULL), data(value) {}
~QueueNode() { /*if (next) delete next;*/ }
QueueNode *next;
T data;
};
public:
AtomicQueue()
{
m_front = new QueueNode(T());
m_tail.store(m_front);
m_divider.store(m_front);
}
~AtomicQueue()
{
QueueNode *node = m_front;
while(node->next)
{
QueueNode *n = node->next;
delete node;
node = n;
}
}
void push(const T& value)
{
m_tail.load()->next = new QueueNode(value);
m_tail = m_tail.load()->next; // This moves the QueueNode into the atomic pointer, making it safe :)
while(m_front != m_divider.load())
{
QueueNode *tmp = m_front;
m_front = m_front->next;
delete tmp;
}
}
bool peek(T& result)
{
if (m_divider.load() != m_tail.load())
{
// Problem area
QueueNode *next = m_divider.load()->next;
if (next)
{
result = next->data;
return true;
}
}
return false;
}
bool pop(T& result)
{
bool res = this->peek(result);
if (res)
{
m_divider = m_divider.load()->next;
}
return res;
}
private:
QueueNode *m_front;
QAtomicPointer<QueueNode> m_divider, m_tail;
};
#endif // ATOMICQUEUE_HPP
Proszę odpowiedzieć na własne pytanie, jeśli znalazłeś odpowiedź. –
Bardzo dziękuję za wdrożenie kolejki atomowej: D –
Moja przyjemność. Teraz ma już kilka lat, ale mam nadzieję, że nadal będzie równała się z resztą Qt/C++. Nie jestem pewien, czy nowy standard C++ może oferować lepsze funkcje. – OzBarry