无锁队列的实现

无锁队列的实现

无锁队列https://coolshell.cn/articles/8239.html

  链表实现   cas

                   入队时注意 lock free(锁无关)问题 防止死锁

                            Tail

                   出队

                            如果是用了指针小心 aba问题(指针内存重用)

                                     解决 

            1、使用double-CAS(双保险的CAS)

              1)一次用CAS检查双倍长度的值,前半部是指针,后半部分是一个计数器。

              2)只有这两个都一样,才算通过检查,要吧赋新的值。并把计数器累加1。

            2、不让那个地址重用

              使用结点内存引用计数,就是那块内存的引用加1阻止被回收,搞完后再减一,这两个操作都是原子操作

         数组实现   环形数组(求模) cas 原子加减

                   声明两个计数器,一个用来计数入队的次数,一个用来计数出队的次数。

                   入队

        定位TAIL的位置,double-CAS方法把(TAIL, EMPTY) 更新成 (x, TAIL)。如果找不到(TAIL, EMPTY),则说明队列满了。再原子入队加

                   出队

        定位HEAD的位置,把(HEAD, x)更新成(EMPTY, HEAD),并把x返回。同样需要注意,如果x是TAIL,则说明队列为空。再原子出队加

 

 

 

Linux内核的kfifo[编辑]

在Linux内核文件kfifo.h和kfifo.c中,定义了一个先进先出圆形缓冲区实现。如果只有一个读线程、一个写线程,二者没有共享的被修改的控制变量,那么可以证明这种情况下不需要并发控制。kfifo就满足上述条件。kfifo要求缓冲区长度必须为2的幂。读、写指针分别是无符号整型变量。把读写指针变换为缓冲区内的索引值,仅需要“按位与”操作:(指针值 按位与 (缓冲区长度-1))。这避免了计算代价高昂的“求余”操作。且下述关系总是成立:

读指针 + 缓冲区存储的数据长度 == 写指针

即使在写指针达到了无符号整型的上界,上溢出后写指针的值小于读指针的值,上述关系仍然保持成立(这是因为无符号整型加法的性质)。 kfifo的写操作,首先计算缓冲区中当前可写入存储空间的数据长度:

len = min{待写入数据长度, 缓冲区长度 - (写指针 - 读指针)}

然后,分两段写入数据。第一段是从写指针开始向缓冲区末尾方向;第二段是从缓冲区起始处写入余下的可写入数据,这部分可能数据长度为0即并无实际数据写入。

 

 

内存屏蔽 从技术上来讲,这即是说一个“顺序一致”的模型,其中读和写操作的执行顺序跟它们在源代码中的顺序是完全一样的[8]。

duumy 伪头部,duumy->next才是真正存有数据的head

posted @ 2019-03-23 19:45 ff_d 阅读( ...) 评论( ...) 编辑 收藏
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
下面是一个基于C++11的无锁队列实现及测试代码: ```c++ #include <atomic> #include <memory> template <typename T> class LockFreeQueue { private: struct Node { std::shared_ptr<T> data; Node* next; Node() : next(nullptr) {} }; std::atomic<Node*> head; std::atomic<Node*> tail; public: LockFreeQueue() : head(new Node), tail(head.load()) {} LockFreeQueue(const LockFreeQueue&) = delete; LockFreeQueue& operator=(const LockFreeQueue&) = delete; ~LockFreeQueue() { while (Node* const old_head = head.load()) { head.store(old_head->next); delete old_head; } } void push(T new_value) { std::shared_ptr<T> new_data(std::make_shared<T>(std::move(new_value))); Node* new_node = new Node; Node* old_tail = tail.load(); old_tail->data.swap(new_data); old_tail->next = new_node; tail.store(new_node); } std::shared_ptr<T> pop() { Node* old_head = head.load(); while (old_head != tail.load()) { if (head.compare_exchange_weak(old_head, old_head->next)) { std::shared_ptr<T> res; res.swap(old_head->data); delete old_head; return res; } } return std::shared_ptr<T>(); } }; #include <iostream> #include <thread> int main() { LockFreeQueue<int> queue; std::thread t1([&queue]() { for (int i = 0; i < 10; ++i) { queue.push(i); std::this_thread::sleep_for(std::chrono::milliseconds(100)); } }); std::thread t2([&queue]() { for (int i = 0; i < 10; ++i) { auto val = queue.pop(); if (val) { std::cout << "t2: " << *val << std::endl; } else { std::cout << "t2: queue is empty" << std::endl; } } }); std::thread t3([&queue]() { for (int i = 0; i < 10; ++i) { auto val = queue.pop(); if (val) { std::cout << "t3: " << *val << std::endl; } else { std::cout << "t3: queue is empty" << std::endl; } } }); t1.join(); t2.join(); t3.join(); return 0; } ``` 该无锁队列使用两个指针 head 和 tail 分别指向队列头和尾,使用 std::atomic 实现原子操作。push() 使用 std::shared_ptr 存储数据,同时创建新的节点并把 tail 指针指向新的节点。pop() 方法则从 head 开始遍历队列,如果 head 没有指向 tail,就尝试原子地更新 head 指针,将数据保存到 std::shared_ptr 中并返回。如果更新失败,则说明队列已经被其他线程修改,需要重新遍历。 测试代码中创建了三个线程,其中一个线程不断 push(),另外两个线程不断 pop(),当队列为空时输出提示信息。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值