simple c++ 无锁队列

简洁实现无锁队列

使用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;
}
相关推荐
山山而川粤2 小时前
母婴用品系统|Java|SSM|JSP|
java·开发语言·后端·学习·mysql
_君莫笑2 小时前
【视频】将yuv420p的一帧数据写入文件
c++·音视频·yuv420p
迷失蒲公英3 小时前
XML与Go结构互转实现(序列化及反序列化)
xml·开发语言·golang
测试盐3 小时前
c++编译过程初识
开发语言·c++
赖赖赖先生3 小时前
fastadmin 框架 生成qr code 二维码图片,PHP 7.4版本
开发语言·php
玉红7774 小时前
R语言的数据类型
开发语言·后端·golang
夜斗(dou)4 小时前
node.js文件压缩包解析,反馈解析进度,解析后的文件字节正常
开发语言·javascript·node.js
觅远4 小时前
python+PyMuPDF库:(一)创建pdf文件及内容读取和写入
开发语言·python·pdf
神雕杨5 小时前
node js 过滤空白行
开发语言·前端·javascript
lvbu_2024war015 小时前
MATLAB语言的网络编程
开发语言·后端·golang