无锁队列

Posted perfy576

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了无锁队列相关的知识,希望对你有一定的参考价值。

1 伪命题

这本身是个伪命题。

多线程之间使用队列是一定需要做到同步的。也就是说一定是需要同步手段的,一定要在一个线程读写的时候,阻塞另一个线程。既然不然用锁,那就是用原子变量吧。

 

2 CAS

3 实现

队列,这里使用链表来实现

struct ListNode
{
 ListNode *next;
 int val;     
 ListNode(int x):val(x),ListNode(nullptr){}
};

class UnlockQueue
{
public:
   void push(int x);
   int pop(); 
private:
  ListNode *push_start;//
  ListNode *pop_start;
};

  

然后,考虑push,在push的时候,要在push_start的next上加节点,此时push_start->next是等于nullptr的。

void UnlockQueue::push(int x)
{
 ListNode *n=new ListNode(x);
 ListNode *tmp=push_start;
  
 while(cas(push_start->next,nullptr,n)!=true)
 {
   tmp=push_start;
 }
  push_start=n;
}

  

解释一下,创建要添加的节点以后,就用cas轮序push_start->next,当等于nullptr的时候,就将next设置为n。

因为是个原子操作,因此同一时刻,就只有这个线程在设置next。同时,当别的线程设置了next以后,那么next就不在指向nullptr,那么就会一直循环。

当设置完以后,就将push_start设置为n,此时其他线程,tmp指向改变,真正的指向尾部,因此其他线程就可在cas通过了。

因此本质上还是锁。但是这里使用了轮询。

原因在于,这个设置过程时间很短暂,所以没有必要使用锁。

 

这里,类的两个字段,可以不用加上volatile关键字修饰,因为别的线程会改变自己正在使用的数据。因为在cas的实现上,这两个数据成员所处的内存一直是由某个cpu独享的。因此不会有其他核心去改变这个数据。

 

看到这里估计也就明白了,这其实是一个自己实现的自旋锁,嗯,自旋锁,使用轮询的方式,不让进程阻塞。

 

对应的pop操作,同理,也是这样的思想。

  

4 CAS原理

 

以上是关于无锁队列的主要内容,如果未能解决你的问题,请参考以下文章

CAS无锁队列的实现

无锁队列的实现

高性能无锁队列,代码注释

是否存在乐观的无锁FIFO队列实现?

在 Folly 的无锁 SPSC 队列中使用 std::memory_order_consume

C ++ 11中无锁的多生产者/消费者队列