队列是我们非常常用的数据结构,用来提供数据的写入和读取功能,而且通常在不同线程之间作为数据通信的桥梁。不过在将无锁队列的算法之前,需要先了解一下CAS(compare and swap)的原理。由于多个线程同时操作同一个数据,其中肯定是存在竞争的,那么如何能够针对同一个数据进行操作,而且又不用加锁呢? 这个就需要从底层,CPU层面支持原子修改操作,比如在X86的计算机平台,提供了XCHG指令,能够原子的交互数值。

  从开发语言的层面,比如C++11中,就提供了atomic_compare_exchange_weak函数,来实现CAS。

  1. lockless queue,enqueue,dequeue操作的算法

  (1) enqueue算法:

  

  enqueue时,先将需要加入队尾的数据创建出来,然后在一个循环操作中,将数据加入队尾,如果加入失败,那么就更新当前的队尾指针,直到加入成功,然后循环结束。最后调整队尾指针。

  (2) dequeue算法

  

  dequeue时,在循环操作中,使用CAS将队列头指针,变成头指针的下一个指针,如果失败,持续操作直到成功。最后返回头指针指向的值。

  2. ABA问题,及解决办法

   从上面的算法中,可以看出采用CAS方式实现的无锁队列的算法过程,不过由于CAS操作本身的特殊性(通过判断当前被变换的值,是否发生过变化),可能会在某些情况下引起ABA问题。

  那么首先什么是ABA问题呢? wiki上有这样一个说明的例子:

  Natalie is waiting in her car at a red traffic light with her children. Her children start fighting with each other while waiting, and she leans back to scold them. Once their fighting stops, Natalie checks the light again and notices that it's still red. However, while she was focusing on her children, the light had changed to green, and then back again. Natalie doesn't think the light ever changed, but the people waiting behind her are very mad and honking their horns now.

  意思就是说,Natalie在等红灯的时候,由于回头管孩子,错过了绿灯,等她再回过头看信号灯的时候,又是红灯了。

  这其实就是一个ABA问题,虽然中间信号灯发生了变化,但是Natalie却不知道。

  

  用C++中的一个stack来说明,stack代码如下:

/* Naive lock-free stack which suffers from ABA problem.*/class Stack {std::atomic<Obj*> top_ptr;//// Pops the top object and returns a pointer to it.//
    Obj* Pop() {while(1) {Obj* ret_ptr = top_ptr;if (!ret_ptr) return std::nullptr;// For simplicity, suppose that we can ensure that this dereference is safe// (i.e., that no other thread has popped the stack in the meantime).Obj* next_ptr = ret_ptr->next;// If the top node is still ret, then assume no one has changed the stack.// (That statement is not always true because of the ABA problem)// Atomically replace top with next.if (top_ptr.compare_exchange_weak(ret_ptr, next_ptr)) {return ret_ptr;}// The stack has changed, start over.
      }}//// Pushes the object specified by obj_ptr to stack.//
    void Push(Obj* obj_ptr) {while(1) {Obj* next_ptr = top_ptr;obj_ptr->next = next_ptr;// If the top node is still next, then assume no one has changed the stack.// (That statement is not always true because of the ABA problem)// Atomically replace top with obj.if (top_ptr.compare_exchange_weak(next_ptr, obj_ptr)) {return;}// The stack has changed, start over.
      }}};

  假设,stack初始化为top → A → B → C

  线程1先执行 

ret = A;
next = B;

  然后在线程1执行compare_exchange_weak之前被中断,换成线程2执行。

{ // 线程2先pop出Aret = A;next = B;compare_exchange_weak(A, B)  // Success, top = Breturn A;} // Now the stack is top → B → C{ // 线程2再pop出Bret = B;next = C;compare_exchange_weak(B, C)  // Success, top = Creturn B;} // Now the stack is top → Cdelete B;{ // 最后线程2将A再push进stackA->next = C;compare_exchange_weak(C, A)  // Success, top = A}

  当线程2执行完所有这些操作之后,换成线程1执行,线程1的compare_exchange_weak是会成功执行的,因为它不知道top_ptr已经被修改过了。

  通常针对ABA问题的解决办法,就是针对操作的数据加上一个原子操作的使用计数,在CAS执行之前,先获取一下计数是否和之前一样,如果不一样,就说明数据已经被修改过了。

转载于:https://www.cnblogs.com/chobits/p/5110624.html

无锁队列以及ABA问题相关推荐

  1. CAS无锁队列的实现

    文章目录 1. 基本原理 2. 代码实现 2.1 使用链表实现无锁队列 2.2 使用数组实现环形无锁队列 3. ABA 问题及解决 4. 参考资料 1. 基本原理 源于1994年10月发表在国际并行与 ...

  2. 基于数组的无锁队列(译)

    2019独角兽企业重金招聘Python工程师标准>>> 1 引言 最近对于注重性能的应用程序,我们有了一种能显著提高程序性能的选择:多线程.线程的概念实际上已经存在了很长时间.在过去 ...

  3. .net 延时操作_锁、CAS操作和无锁队列的实现

    (给算法爱好者加星标,修炼编程内功) 来源:yishizuofei blog.csdn.net/yishizuofei/article/details/78353722 锁的机制 锁和人很像,有的人乐 ...

  4. 锁、CAS操作和无锁队列的实现

    锁的机制 锁和人很像,有的人乐观,总会想到好的一方面,所以只要越努力,就会越幸运:有的人悲观,总会想到不好的一方面,患得患失,所以经常会做不好事.我一直把前一个当作为我前进的动力和方向,快乐充实的过好 ...

  5. 无锁队列的几种实现及其性能对比

    一.无锁队列用在什么样的场景? 当需要处理的数据非常多,比如行情数据,一秒处理非常多的数据的时候,可以考虑用无锁队列.但是如果一秒只需要处理几百或者几千的数据,是没有必要考虑用无锁队列的.用互斥锁就能 ...

  6. 无锁队列 java_无锁队列的总结

    首次接触无锁数据结构的设计,请各位大佬多多指教~~~ CAS(Compare && Swap)原子操作 CAS是无锁(lock free)的数据结构的基础.用伪代码描述: input: ...

  7. linux无锁队列性能对比,无锁队列的一种实现

    队列作为最常用的基础数据结构之一,相信大家都已经非常非常熟悉了,这里省略关于队列的介绍.在平时开发中队列的出现频率非常非常高,因此我们也会很关心队列的性能问题.当并发访问队列时,队列的性能往往受到同步 ...

  8. 无锁CAS/无锁队列

    高并发,读写十分频繁,会使用CAS 1 互斥锁 自旋锁 原子操作 锁住的代码耗时短:counter++操作,自旋锁有优势 锁住的代码耗时长:for_add操作,自旋锁无优势[因为在等待的时候消耗的CP ...

  9. 你应该知道的高性能无锁队列Disruptor

    1.何为队列 听到队列相信大家对其并不陌生,在我们现实生活中队列随处可见,去超市结账,你会看见大家都会一排排的站得好好的,等待结账,为什么要站得一排排的,你想象一下大家都没有素质,一窝蜂的上去结账,不 ...

最新文章

  1. P1034 矩形覆盖
  2. pandas.get_dummies函数用法详细解答(实践)
  3. VTK:简单操作之DistancePointToLine
  4. 《Booth 空箱》发售一周年回顾
  5. 【CodeForces - 798D】Mike and distribution (思维构造,贪心,黑科技)
  6. 洛谷P3405 [USACO16DEC]Cities and States省市
  7. Java Web项目在Linux服务器自动化部署续-整合Bamboo
  8. 公众号小tips(持续更新)
  9. 修改路由器mac地址_你知道吗:路由器转发报文时,会剥掉MAC地址,重新封装
  10. python websocket爬虫_python根据websocket抓取斗鱼弹幕和礼物消息
  11. 中标麒麟系统安装步骤
  12. 集成支付宝,跳转到支付宝后显示的不是支付页面
  13. 重力场和稳态海洋环流探测器(GOCE)
  14. 浙江理工c语言复试试题,2016年浙江理工大学信息学院C语言程序设计复试笔试最后押题五套卷...
  15. linux fprintf sprintf 函数
  16. PostgreSQL时区转换问题UTC与北京时间转换
  17. 金融科技之:互联网贷款企业运营规范
  18. wr741n wr841n openwrt ,AR9331/AR9341网口修正方法
  19. [ZUCC 英语周测]Quiz B-3-8
  20. 彻底搞懂Python中的zip()

热门文章

  1. IoC 容器和 Dependency Injection 模式[转]
  2. redux源码分析之二:combineReducers.js
  3. 东软是如何看待“人与资产”管理的转型
  4. 【索引】反向索引--条件 范围查询(二)
  5. Android Studio IDE Out of Memory
  6. centos一键安装包无法创建vhost
  7. systemverilog编译介绍
  8. 【python】热力图绘制: intensity_heatmap,density_heatmap
  9. 一点对 KL 散度的理解
  10. SOA架构设计和相关案例分析