节点结构:
template<typename T>
struct Node {
T data;
std::atomic<std::shared_ptr<Node<T>>> next;
Node(const T& val) : data(val), next(nullptr) {}
Node() : data(T{}), next(nullptr) {}
};
使用哨兵节点作为头节点,方便插入和删除的操作保持一致
template<typename T>
class LockFreeQueue {
private:
// 使用原子共享指针来管理节点,自动处理内存回收,避免ABA和UAF问题
std::atomic<std::shared_ptr<Node<T>>> head;
std::atomic<std::shared_ptr<Node<T>>> tail;
public:
LockFreeQueue() {
auto dummyNode = std::make_shared<Node>();
head.store(dummyNode);
tail.store(dummyNode);
}
enqueue:入队操作
new_node = make_shared<Node<T>>(val); // (1)
tail->next = new_node; // (2)
tail = new_node; // (3)
注意:只有当(2)成立了才会去执行(3)
那么思考多线程情况下应该如何去操作?
迭代版本1:
auto newNode = std::make_shared<Node<T>> val;
// 先获得:tail->next
auto oldTail = tail.load();
auto next = oldTail->next;
while(true){
//现在尝试去执行:tail->next = new_node;
if(oldTail->next.compare_exchange_weak(next,newNode)){
//(2)成功了,那么去执行(3).
// 此外,此时可能发生一件事:我把节点插入进去了,我还没修改tail,我的节点就切换了
// 此时产生的影响:当前语句的执行,以及auto next = oldTail->next;,我的next可能不是null了
// 去看迭代版本2
if(tail.compare_exchange_weak(oldTail,newNode)){
return;
}
}
}
迭代版本2:
auto newNode = std::make_shared<Node<T>> val;
while(true){
auto oldTail = tail.load();
auto next = oldTail->next;
if(next==null){
if(oldTail->next.compare_exchange_weak(next,newNode)){
if(tail.compare_exchange_weak(oldTail,newNode)){
return;
}
}
}else{
// next 不为空,说明有其他线程完成了链接但没移动 tail 指针
// 我们把 tail 指针向后移动
tail.compare_exchange_strong(oldTail, next);
}
}
-
致命错误
- T1:线程插入节点,但是没有修改tail
- T2:发现T1没有修改tail,那么就去尝试修改tail
- T1:返回用户,发现我的tail已经被别人修改了,我不需要修改,但是并没有直接return,而且继续尝试
while继续插入节点newNode。导致线程T1出问题了。
那么也就是,我无论tail.compare_exchange_weak(oldTail,newNode)执行是否成功其实都说明一件事,当前节点插入成功,无需额外处理了。
迭代版本3:
auto newNode = std::make_shared<Node<T>> val;
while(true){
auto oldTail = tail.load();
auto next = oldTail->next;
if(next==null){
if(oldTail->next.compare_exchange_weak(next,newNode)){
tail.compare_exchange_weak(oldTail,newNode))
return;
}
}else{
// next 不为空,说明有其他线程完成了链接但没移动 tail 指针
// 我们把 tail 指针向后移动
tail.compare_exchange_strong(oldTail, next);
}
}
- 代码正确
迭代版本4:
void enqueue(const T& val) {
auto newNode = std::make_shared<Node>(val);
for (;;) {
auto oldTail = tail.load();
auto next = oldTail->next;
if (oldTail == tail.load()) {
if (next == nullptr) {
if (oldTail->next.compare_exchange_weak(next, newNode)) {
tail.compare_exchange_strong(oldTail, newNode);
return;
}
} else {
tail.compare_exchange_strong(oldTail, next);
}
}
}
}
- 正确:
if (oldTail == tail.load())可以用来判断auto oldTail = tail.load(); auto next = oldTail->next;直接是不是出现了问题。