无锁队列
无锁队列一般指的是通过CAS操作来保证队列的线程安全性问题,而不会使得线程陷入到内核,以避免用户态与内核态的切换开销;
实现原理
- 采用链表,实现基于自旋锁CAS的无界队列
- 自旋锁方式,对head/tail自旋为NULL 表示成功获取自旋锁:
2.1. 在push函数中,对tail成功CAS为NULL 表示当前线程获取tail自旋锁成功,并设置tail的next节点为push的元素,解锁tail,即将tail进行CAS为tail->next;
2.2. 在tryPop函数中,对head成功CAS为NULL 表示当前线程获取head自旋锁成功,并需要判断当前数组是否为空,如果为空,则解锁并返回为false;否则成功,则pop出数据head->next->val,最后解锁,即将head进行CAS为head->next; - 为什么这里一定需要count变量记录当前队列中元素的数量?
head和tail被用来进行加锁,可以临时设置为NULL,表示加锁,这时候head和tail可能不会指向的应该指向节点,因此。需要count计数器复辅助判断当前队列中的元素; - T应当是trival的,是否lock_free依赖于std::atomic是否lock_free;
- 存在ABA问题,但不会影响线程安全性;
源码
#pragma once
#include<iostream>
#include<atomic>
#include<thread>
#include<assert.h>
//保证T应当是trival
//基于链表的无界无锁队列
template<typename T>
class LockFreeLinkedQueue {
public:
//保证初始化在单线程下完成
LockFreeLinkedQueue() {
Node* node = new Node(Empty);
head.store(node);
tail.store(node);
islockfree = node->val.is_lock_free();
}
~LockFreeLinkedQueue() {
T val = Empty;
while (tryPop(val));
Node* node = head.load();
if (node != NULL)
delete node;
}
bool is_lock_free() {
return islockfree;
}
bool isEmpty() {
return count.load() == 0; }
bool isFull() {
return false; }
//push操作,CAS加tail锁
bool push(T val);
//pop操作,CAS加head锁
bool tryPop(T& val);
//不建议使用,当队列中无元素时,会自旋
T pop();
private:
struct Node {
std::atomic<T> val;
std::atomic<Node*>next = NULL;
Node(T val) :val(val) {
}
};
const T Empty = 0;
std::atomic<int>count = {
0 }; //计数器
std::atomic<Node*>head; //头结点
std::atomic<Node*>tail; //尾结点