7

我正在 Qt 中构建一个相当复杂的应用程序,它可以动态加载动态库并将它们作为线程运行,它们必须尽快在彼此之间传递信息,所以我认为原子队列将是我最好的情况,所以这是AtomicQueue.hpp 文件:

#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

在我推送和弹出一个项目后队列中断,我不知道为什么。我对原子风格的线程安全编程很陌生,所以我很可能只是不了解其中的特定方面。bool AtomicQueue::peek在调试模式下运行时,我的函数中出现 SEGSEV 段错误result = next->data

谁能指出我做错了什么?

更新:

所以我解决了这个问题,它在我的 QueueNode 析构函数中。本质上,当我删除一个节点时,它会尝试清理所有其他节点,然后给我一些不同的修复它的路径:

  1. 在 QueueNode 中设置一个标志,告诉它不要删除所有->next节点
  2. 在删除前分离前端节点
  3. 允许 AtomicQueue 管理节点清理

我选择了第三个选项,因为它在推送新节点时已经进行了一些清理,所以这里是任何感兴趣的人的固定类:

#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
4

1 回答 1

0

注意:一旦我在代码中发现问题,这只是从我的问题中复制而来。感谢@peter-k 指出这并没有得到正式答复。

所以我解决了这个问题,它在我的 QueueNode 析构函数中。本质上,当我删除一个节点时,它会尝试清理所有其他节点,然后给我一些不同的修复它的路径:

  1. 在 QueueNode 中设置一个标志,告诉它不要删除所有->next节点
  2. 在删除前分离前端节点
  3. 允许 AtomicQueue 管理节点清理

我选择了第三个选项,因为它在推送新节点时已经进行了一些清理,所以这里是任何感兴趣的人的固定类:

#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
于 2016-08-17T20:08:58.093 回答