Boost Lockfree
flyfish 2014-9-30
首先lockfree ,如何去理解这个free
引用自《英语学习漫谈》
free诚然可以当自由来理解,但自由并没有触及到 free的核心概念,所以你在很多场合看到free的时候,会产生理解困难。那年迈阿密热打进NBA总决赛,比赛转播期间电视上不停地打出一个广告,说佛罗里达要成为一个 tobaccofree的州,这显然不是说你在佛罗里达可以随便抽烟,而是此州(所有公共场所)不能抽烟;最近美国的几家航空公司开始考虑在飞机上设立childrenfree区,就是不让小孩儿进去,免得打搅其他旅客;美国的drugstore里出售的软饮料包装上可能会有“alcohol free”的字样,就是说不含酒精。 “自由”是free的引申义。free的核心概念是“无……”。“无”的一般来说是一些会让你不开心的东西,诸如负担、负税、责任、包袱、费用、规则、担忧、焦虑……“自由”本身也是没有负担和阻碍的意思。当free出现在一个词后面的时候,如 alcohol free,就是要把这个词代表的东西干掉了,这个用法才接近free的核心含义。英美人看到free,潜意识只会想到它要干掉的东西,所以free本身并不是一个完整的词。 现在你明白“carefree”是怎么回事了吧?不担心,不关心,不操心,怎会不一身轻松呢?但是,绝对的carefree是不可能的,你不care别人,也没有人会care你。 我曾经犯过把“free”当成“自由”来理解的错误。有一次吃盖浇饭碰到一个美国人,我跟他搭讪,说:“美国是一个free的国家。”他没听听懂,问:“什么free?”我一开始心想他连美国精神都不知道,但马上反应过来了,赶紧加了一句:“free去干很多事。”他这才明白。可见“free”单独出现时并不代表“自由”,“自由”只是“free”可以达到的许多结果之一。
与锁无关的数据结构不是依赖于锁和互斥来确保线程安全。
Lockfree的重要操作就是CAS(Compare And Set)原子操作
原子操作就是多个线程访问同一个资源时,有且仅有唯一 一个线程对该资源进行操作
BOOST中的宏定义
BOOST_ATOMIC_DETAIL_X86_HAS_CMPXCHG8B
BOOST_ATOMIC_DETAIL_X86_HAS_CMPXCHG16B
cmpxchg:比较并交换,也就是
伪代码
- bool CAS(int* p,int nOld,int nNew)
- {
- bool bRet=false;
- if (*p == nOld)
- {
- *p=nNew;
- bRet=true;
- }
- return bRet;
- }
CAS操作产生的ABA问题
两个线程P1和P2
P1读变量A
P1中断,
P2开始运行,读变量A,更改为B,又更改为A,
P2执行完
P1继续操作,A还是A,P1认为什么也没有发生
例如
栈 (Stack)是一种后进先出(last in first off,LIFO)的数据结构,只允许在栈顶操作。
假设有一个栈,有三个元素
A
B
C
线程P1,P2对该栈进行操作
P1 读栈顶A
P1 中断
P2 开始运行,A出栈,B出栈,A压栈(删除B)
P1 继续执行,A还是A
P1认为该栈的结构还是ABC,而此时栈里只有两个元素
就像小时候在家偷看电视,防止电视过热被父母发现,看一会儿就关上,等会儿再看。
当听见开门的声音时,以迅雷不及掩耳之势,换回开始的频道,调回音量图标的格数,关上电视,放回遥控器原来的位置并注意遥控器的朝向来表明没有看电视。
64位平台的处理方案
看boost::lockfree::detail::tagged_ptr代码
解决方式1
文件tagged_ptr_dcas.hpp
简化代码
- template <class T>
- class tagged_ptr
- {
- public:
- typedef std::size_t tag_t;
- protected:
- T * ptr;
- tag_t tag;
- };
ABA问题解决方式是一个std::size_t类型的tag+指针,但不是所有平台都支持。
解决方式2
文件tagged_ptr_ptrcompression.hpp
简化代码
- #if defined (__x86_64__) || defined (_M_X64)
- class tagged_ptr
- {
- typedef boost::uint64_t compressed_ptr_t;
- public:
- typedef boost::uint16_t tag_t;
- private:
- union cast_unit
- {
- compressed_ptr_t value;
- tag_t tag[4];
- };
- static const int tag_index = 3;
- static const compressed_ptr_t ptr_mask = 0xffffffffffffUL; //(1L<<48L)-1;
- ......
- }
- #else
- #error unsupported platform
- #endif
早期X86-64处理器不支持cmpxchg16b指令
在X86-64平台上,48位是地址,剩下的16位是ABA预防tag
代码
static const compressed_ptr_t ptr_mask = 0xffffffffffffUL; //(1L<<48L)-1;
0xffffffffffff二进制就是48个1。
tag就像一个版本号,通过tag,线程可以知道操作的数据已经发生了变化。