dcm*_*m88 3 c++ stack multithreading lock-free c++11
我已经看到了几个过于复杂的(在我看来很明显)在c ++中使用无锁堆栈的实现(使用像这里的标签),我想出了一个简单但仍然有效的实现.因为我无法在任何地方找到这个实现(我已经看到Push函数的实现类似于我所做的而不是Pop),我猜它在某种程度上是不正确的(很可能是ABA案例失败):
template<typename Data>
struct Element
{
Data mData;
Element<Data>* mNext;
};
template<typename Data>
class Stack
{
public:
using Obj = Element<Data>;
std::atomic<Obj*> mHead;
void Push(Obj *newObj)
{
newObj->mNext = mHead.load();
//Should I be using std::memory_order_acq_rel below??
while(!mHead.compare_exchange_weak(newObj->mNext, newObj));
}
Obj* Pop()
{
Obj* old_head = mHead.load();
while (1)
{
if (old_head == nullptr)
return nullptr;
//Should I be using std::memory_order_acq_rel below??
if(mHead.compare_exchange_weak(old_head, old_head->mNext)) ///<<< CL1
return old_head;
}
}
};
Run Code Online (Sandbox Code Playgroud)
我假设Push和Pop的调用者将负责内存分配和释放.另一种选择是制作上述Push和Pop私有方法,并使用新的公共函数来处理内存分配并在内部调用这些函数.我相信这个实现中最棘手的部分是我用"CL1"标记的行.我认为它是正确的并且仍然适用于ABA案例的原因如下:
让我们说ABA案件确实发生了.这意味着"CL1"处的mHead将等于old_head,但是它们指向的对象实际上与我将mHead分配给它时最初指向的old_head不同.但是,我认为即使它是一个不同的对象我们仍然可以,因为我们知道它是一个有效的"头".old_head指向与mHead相同的对象,因此它是堆栈的有效头部,这意味着old_head-> mNext是有效的下一个头.因此,将mHead更新为old_head-> mNext仍然是正确的!
总结一下:
那么,我的实施是否有效?我错过了什么?