動的ライブラリを動的にロードしてスレッドとして実行できるかなり複雑なアプリケーションを 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
1 つのアイテムをプッシュ アンド ポップすると、キューが中断されますが、その理由がわかりません。私は Atomic スタイルのスレッドセーフ プログラミングにまったく慣れていないので、この特定の側面を理解していない可能性は十分にあります。bool AtomicQueue::peek
デバッグ モードで実行すると、関数で SEGSEV segfault が発生しますresult = next->data
。
誰かが私が間違っていることを指摘できますか?
アップデート:
そこで、QueueNode デストラクタにあった問題を修正しました。基本的に、ノードを削除すると、それ以降のすべてのノードをクリーンアップしようとするため、それを修正するためのいくつかの異なるルートが得られました。
->next
すべてのノードを削除しないように指示するフラグを QueueNode に設定します。- 削除する前にフロント ノードをデタッチする
- AtomicQueue がノードのクリーンアップを管理できるようにする
新しいノードをプッシュしている間にすでにいくつかのクリーンアップを行っていたので、私は 3 番目のオプションを選択しました。
#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