2013-09-30 15 views
7

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:

  1. Ustaw flagę w QueueNode, który mówi, że nie, aby usunąć wszystkie ->next węzłów
  2. Odłącz węzeł z przodu przed usunięciem go
  3. 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 
+2

Proszę odpowiedzieć na własne pytanie, jeśli znalazłeś odpowiedź. –

+0

Bardzo dziękuję za wdrożenie kolejki atomowej: D –

+0

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

Odpowiedz

0

Uwaga: To jest po prostu kopiowane z moim pytaniem raz zorientowali się problem w moim kodu. Dzięki @ peter-k za wskazanie tego nie otrzymałem oficjalnej odpowiedzi.

Naprawiłem problem, który był w moim destruktorze QueueNode. 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:

  1. Ustaw flagę w QueueNode, który mówi, że nie, aby usunąć wszystkie ->next węzłów
  2. Odłącz węzeł z przodu przed usunięciem go
  3. 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