简洁实现无锁队列
使用compare_exchange_strong 和 compare_exchange_weak,在 C++ 中,compare_exchange_strong 和 compare_exchange_weak 是 std::atomic 类型的成员函数,用于原子地比较和交换操作。以下实现适合单生产者,单消费者,否则效率不佳
1 、compare_exchange_strong
功能
尝试将原子对象的值与预期值进行比较,如果相等,则将原子对象的值更新为新值;如果不相等,则将预期值更新为原子对象的当前值。这个操作是原子性的,意味着在多线程环境下,它不会被其他线程中断,从而保证了数据的一致性和线程安全。
特点
总是进行严格的比较和交换操作。如果比较失败,它会明确地返回 false,并且不会修改预期值。在循环中使用时,可以确保在比较失败的情况下,程序可以正确地处理并重新尝试比较和交换操作。
c
#include <iostream>
#include <atomic>
std::atomic<int> value(0);
void compareExchangeStrongExample() {
int expectedValue = 0;
int newValue = 10;
bool success = value.compare_exchange_strong(expectedValue, newValue);
if (success) {
std::cout << "compare_exchange_strong succeeded. New value: " << value << std::endl;
} else {
std::cout << "compare_exchange_strong failed. Current value: " << value << std::endl;
}
}
2、compare_exchange_weak
功能
与 compare_exchange_strong 类似,尝试将原子对象的值与预期值进行比较,如果相等,则将原子对象的值更新为新值;如果不相等,则将预期值更新为原子对象的当前值。
特点
可能会出现虚假失败的情况。也就是说,即使原子对象的值与预期值相等,这个操作也可能返回 false。这是因为在某些硬件平台上,原子比较和交换操作可能会受到其他因素的影响,导致偶尔出现虚假失败。由于可能出现虚假失败,通常在循环中使用 compare_exchange_weak,以便在失败时重新尝试比较和交换操作。
c
#include <iostream>
#include <atomic>
std::atomic<int> value(0);
void compareExchangeWeakExample() {
int expectedValue = 0;
int newValue = 10;
while (true) {
bool success = value.compare_exchange_weak(expectedValue, newValue);
if (success) {
std::cout << "compare_exchange_weak succeeded. New value: " << value << std::endl;
break;
} else {
// 比较失败,重新尝试
std::cout << "compare_exchange_weak failed. Current value: " << value << std::endl;
}
}
}
数据结构
创建需要保存的数据结构
c
struct mydata
{
int a = 0;
std::string message;
uint8_t* data = NULL;
size_t datalen = 0;
mydata(int ina,std::string in,uint8_t* indata, size_t len)
{
a = ina;
message = in;
if (indata != NULL)
{
data = new uint8_t[len];
memcpy(data, indata, len);
datalen = len;
}
}
~mydata()
{
if (data != NULL)
delete[]data;
}
};
创建模板类
值得注意的点就是使用weak必须使用循环,如果不循环,则需要处理失败如何做,这里简单地就循环处理,因为一般来说,插入是必须得,生产线程不能停顿
c
template<typename T>
class LockFreeQueue {
private:
struct Node {
T data;
Node* next;
Node() : next(nullptr) {}
};
std::atomic<Node*> head;
std::atomic<Node*> tail;
public:
LockFreeQueue() {
Node* dummy = new Node();
head.store(dummy);
tail.store(dummy);
}
~LockFreeQueue() {
while (Node* oldHead = head.load()) {
head.store(oldHead->next);
delete oldHead;
}
}
void push(const T& item) {
//std::shared_ptr<T> newData = std::make_shared<T>(item);
Node* newNode = new Node();
newNode->data = item;
Node* oldTail = tail.load();
// 使用原子操作将新节点添加到队列尾部
while (!tail.compare_exchange_weak(oldTail, newNode)) {
// 如果交换失败,说明其他线程已经修改了 tail,重新获取 tail 的值
// weak 就是需要循环,不过可以修改成为跳出,则注意失败处理
}
// 更新旧的尾节点的 next 指针
oldTail->next = newNode;
}
bool pop(T& result) {
Node* oldHead = head.load();
if (oldHead == tail.load()) {
// 队列为空
return false;
}
// 使用原子操作尝试获取队列头部节点
if (head.compare_exchange_weak(oldHead, oldHead->next)) {
result = oldHead->next->data;
delete oldHead;
return true;
}
// 如果交换失败,说明其他线程已经修改了 head,返回 false
return false;
}
T pop() {
Node* oldHead = head.load();
if (oldHead == tail.load()) {
// 队列为空
return NULL;
}
// 使用原子操作尝试获取队列头部节点
if (head.compare_exchange_weak(oldHead, oldHead->next)) {
T result = oldHead->next->data;
delete oldHead;
return result;
}
// 如果交换失败,说明其他线程已经修改了 head,返回 false
return NULL;
}
};
测试程序
读者自行测试吧
c
int main() {
LockFreeQueue<mydata*> queue;
// 生产者线程
std::thread producer([&queue]() {
for (int i = 0; i < 1000; i++) {
std::stringstream s;
s << "this is data " << i;
mydata* data = new mydata(i, s.str(), NULL, 0);
queue.push(data);
std::this_thread::sleep_for(std::chrono::milliseconds(10));
}
});
// 消费者线程
std::thread consumer([&queue]() {
while (true) {
mydata* item = queue.pop();
if (item != NULL) {
std::cout << "Consumed: " << item->message << std::endl;
}
delete item;
std::this_thread::sleep_for(std::chrono::milliseconds(8));
}
});
producer.join();
consumer.join();
std::cout << " end threads " << std::endl;
return 0;
}