无锁队列以及ABA问题
队列是我们非常常用的数据结构,用来提供数据的写入和读取功能,而且通常在不同线程之间作为数据通信的桥梁。不过在将无锁队列的算法之前,需要先了解一下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() {
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() {
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出A
ret = A;
next = B;
compare_exchange_weak(A, B) // Success, top = B
return A;
} // Now the stack is top → B → C
{ // 线程2再pop出B
ret = B;
next = C;
compare_exchange_weak(B, C) // Success, top = C
return B;
} // Now the stack is top → C
delete B;
{ // 最后线程2将A再push进stack
A->next = C;
compare_exchange_weak(C, A) // Success, top = A
}
当线程2执行完所有这些操作之后,换成线程1执行,线程1的compare_exchange_weak是会成功执行的,因为它不知道top_ptr已经被修改过了。
通常针对ABA问题的解决办法,就是针对操作的数据加上一个原子操作的使用计数,在CAS执行之前,先获取一下计数是否和之前一样,如果不一样,就说明数据已经被修改过了。
无锁队列以及ABA问题的更多相关文章
- 一个可无限伸缩且无ABA问题的无锁队列
关于无锁队列,详细的介绍请参考陈硕先生的<无锁队列的实现>一文.然进一步,如何实现一个不限node数目即能够无限伸缩的无锁队列,即是本文的要旨. 无锁队列有两种实现形式,分别是数组与链表. ...
- boost 无锁队列
一哥们翻译的boost的无锁队列的官方文档 原文地址:http://blog.csdn.net/great3779/article/details/8765103 Boost_1_53_0终于迎来了久 ...
- CAS简介和无锁队列的实现
Q:CAS的实现 A:gcc提供了两个函数 bool __sync_bool_compare_and_swap (type *ptr, type oldval, type newval, ...)// ...
- 锁、CAS操作和无锁队列的实现
https://blog.csdn.net/yishizuofei/article/details/78353722 锁的机制 锁和人很像,有的人乐观,总会想到好的一方面,所以只要越努力,就会越幸运: ...
- HashMap的原理与实 无锁队列的实现Java HashMap的死循环 red black tree
http://www.cnblogs.com/fornever/archive/2011/12/02/2270692.html https://zh.wikipedia.org/wiki/%E7%BA ...
- zeromq源码分析笔记之无锁队列ypipe_t(3)
在上一篇中说到了mailbox_t的底层实际上使用了管道ypipe_t来存储命令.而ypipe_t实质上是一个无锁队列,其底层使用了yqueue_t队列,ypipe_t是对yueue_t的再包装,所以 ...
- 无锁队列--基于linuxkfifo实现
一直想写一个无锁队列,为了提高项目的背景效率. 有机会看到linux核心kfifo.h 原则. 所以这个实现自己仿照,眼下linux我们应该能够提供外部接口. #ifndef _NO_LOCK_QUE ...
- Go语言无锁队列组件的实现 (chan/interface/select)
1. 背景 go代码中要实现异步很简单,go funcName(). 但是进程需要控制协程数量在合理范围内,对应大批量任务可以使用"协程池 + 无锁队列"实现. 2. golang ...
- 基于无锁队列和c++11的高性能线程池
基于无锁队列和c++11的高性能线程池线程使用c++11库和线程池之间的消息通讯使用一个简单的无锁消息队列适用于linux平台,gcc 4.6以上 标签: <无> 代码片段(6)[ ...
随机推荐
- CentOS 7 安装、配置、使用 PostgreSQL 9.5及PostGIS2.2
学习CentOS下安装使用PostgreSQL [安装过程] 1.添加RPM yum install https://download.postgresql.org/pub/repos/yum/ ...
- hadoop2.0初识1.1
1.伪分布式hdfs文件系统的搭建(单节点文件系统) 1.1.根据上节的讲解,配置主机映射.jdk和解压hadoop压缩包 1.2.配置namenode 在/opt/modules/hadoop-2. ...
- 怎样让webservice在浏览器远程浏览时像在本地浏览一样有参数输入框
从远程客户端访问服务器上的WebService能够显示,但点击调用相关的方法时显示“只能用于来自本地计算机的请求”,这时提醒我们还需要在服务器进行相关的配置才能让其他机器正常访问该WebService ...
- LTE Module User Documentation(翻译14)——Uplink Power Control(上行功率控制)
LTE用户文档 (如有不当的地方,欢迎指正!) 20 Uplink Power Control(上行功率控制) 上行功率控制功能默认是开启的.用户可以通过设置布尔属性 ns3::LteUePhy: ...
- 12款最佳Linux命令行终端工具, 20款优秀的 Linux 终端仿真器
12款最佳Linux命令行终端工具 如果你跟我一样,整天要花大量的时间使用Linux命令行,而且正在寻找一些可替代系统自带的老旧且乏味的终端软件,那你真是找对了文章.我这里搜集了一些非常有趣的 ...
- Spring事务管理只对出现运行期异常进行回滚
原文:http://blog.csdn.net/abc19900828/article/details/39497631 使用spring难免要用到spring的事务管理,要用事务管理又会很自然的选择 ...
- Python之路 day2 购物车小程序1
#Author:ersa ''' 程序:购物车程序 需求: 启动程序后,让用户输入工资,然后打印商品列表 允许用户根据商品编号购买商品 用户选择商品后,检测余额是否够,够就直接扣款,不够就提醒 可随时 ...
- Retrofit 2.0使用
最近在想能不能把之前项目里的网络请求改下 想通过Retrofit重构下,因为Retrofit完美兼容Rxjava后面会用到Rxjava 所以 开个坑写点 由于网上Retrofit 2.0的架构介绍很详 ...
- Windows下配置OpenGL环境
这里编译工具为VS2012. 首先OpenGL的官网如下链接(英文) http://www.opengl.org http://www.opengl.org/resources/libraries/g ...
- CentOS 下 rpm包与 yum 安装与卸载
rpm包的安装: 1.安装一个包 # rpm -ivh 2.升级一个包 # rpm -Uvh 3.移走一个包 # rpm -e 4.安装参数 --force 即使覆盖属于其它包的文件也强迫安 ...