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原理