实现一个锁无关(lock free)结构~
2009-12-31 16:35
375 查看
我本是菜鸟,但是总是对一些新技术抱着好奇的心态想去研究一下。
前段时间看了Andrei 的文章,略有感想,虽然基本看懂意思,但是还是想亲手实验一下,不过发现代码没能通过编译。相信很多像我这样的菜鸟也是很遗憾,不过我还是试着把这段代码调试成功了,略微修改了一些细节。
代码分为两个文件。
下面一个是:UnLockBaseT.h
下面一个是:UnLockMap.h
代码经过我调试,可以通过VS和g++ 。
前段时间看了Andrei 的文章,略有感想,虽然基本看懂意思,但是还是想亲手实验一下,不过发现代码没能通过编译。相信很多像我这样的菜鸟也是很遗憾,不过我还是试着把这段代码调试成功了,略微修改了一些细节。
代码分为两个文件。
下面一个是:UnLockBaseT.h
//一下程序思路完全参照Andrei Alexandrescu 和 Maged Michael的论文《锁无关的数据结构与Hazard指针》 //并且尽量保持原文注释以及命名,为了让大家和原文做比较。。。中文注释为我添加。 #include <vector> namespace azure { template <class T> bool CAS(T* addr, T expected, T value) { if (*addr == expected) { *addr = value; return true; } return false; } //这个结构体是每个线程都共享的一个链表,作用是如果你要读取从无锁链表中读取一个数据时候,你就必须 //在这个链表登记,表示你现在正在使用这个数据,警告别的线程:别动我的奶酪! struct HPRecPoint { HPRecPoint * pNext_; //活动数,0表示没有,1代表有一个,且最大为1 int active_; // Can be used by the thread // that acquired it void * pHazard_; }; class HPRecType { // Global header of the HP list HPRecPoint * pHead_; public: typedef HPRecPoint Point; HPRecType(); int listLen_; Point * Head() { return pHead_; } //返回一个空闲HPRecType * Point * Acquire(); //释放一个HPRecType节点! void Release(Point* p); }; HPRecType::HPRecType():listLen_(0) { pHead_ = new Point; pHead_->active_ = 0; pHead_->pHazard_ = NULL; pHead_->pNext_ = NULL; } HPRecPoint * HPRecType::Acquire() { // Try to reuse a retired HP record Point * p = pHead_; for (; p; p = p->pNext_) { //下面这个判断作用是查找一个活动数(active_)为0的HPRecType,如果找到,将其active_设置为1并返回 if (p->active_ ||!CAS(&p->active_, 0, 1)) continue; return p; } // Increment the list length //增加链表长度 int oldLen; do { oldLen = listLen_; }while (!CAS(&listLen_, oldLen,oldLen + 1)); // Allocate a new one p = new Point; p->active_ = 1; p->pHazard_ = 0; // Push it to the front Point * old; do { old = pHead_; p->pNext_ = old; } while (!CAS(&pHead_, old, p)); return p; } void HPRecType::Release(Point* p) { //将指针和活动数都置为0,虽然不释放空间,但是代表现在这个节点是空闲的。 p->pHazard_ = 0; p->active_ = 0; } //至此,上面这个登记链表已经完成!现在回顾一下,这个登记链表的作用是登记哪些链表正在被读。 //因为在很多线程同时写无锁数据时候会产生很多原数据的拷贝,而这里是为了防止某些线程释放了 //某些线程正在使用的数据,而造成错误。 //到现在为止,所有的基础准备都已经完成! } //namespace azure
下面一个是:UnLockMap.h
#include "UnLockBaseT.h" #include "iostream" #include <map> #include <vector> #include <algorithm> namespace azure { //垃圾回收门限 #define R 10 //原论文里通过下面句子创建线程私有变量rlist //下面这个vector的作用是存储每个线程需要释放的结构! #ifdef WIN32 #define __per_thread__ __declspec(thread) #else #define __per_thread__ __thread #endif //一下需要特别提到,因为在原文rlist是一个vector,但是这样会报错__per_thread__ //不能修饰有构造函数和析构函数的东西。 //所以这里我改用int类型,因为一个地址也是32位,也就是一个int。 //我程序里面有很多考虑不够周全的。 //限于本人经验能力有限,暂时只能做到这里。 __per_thread__ int rlist[30] = {0}; int rlistLength = 0; template <typename K,typename V> class UnLockMap { typedef std::map<K, V> Map; Map* pMap; void Scan(); void Retire(Map* pOld); HPRecType hpRType; public: typedef HPRecType::Point Point; UnLockMap(); ~UnLockMap(); V Get(const K& K_value); void Update(const K&k, const V&v); }; template <typename K,typename V> UnLockMap<K,V>::UnLockMap() { pMap = new Map; } template <typename K,typename V> UnLockMap<K,V>::~UnLockMap() { delete pMap; } template <typename K,typename V> V UnLockMap<K,V>::Get(const K& K_value) { //获取一个链表登记节点! Point * pRec = hpRType.Acquire(); Map * ptr; do{ ptr = pMap; //下面这一行就是申明:这个指针我在用。 pRec->pHazard_ = ptr; } while (pMap != ptr); // Save Willy //获取数值 V result = (*ptr)[K_value]; // pRec can be released now // because it's not used anymore //读取完成,撤销登记 hpRType.Release(pRec); return result; } template <typename K,typename V> void UnLockMap<K,V>::Update(const K&k, const V&v) { Map* pNew = 0; Map* pOld = pMap; do { delete pNew; pNew = new Map(*pOld); (*pNew)[k] = v; }while (!CAS(&pMap, pOld, pNew)); //下面对pOld放入rlist释放链表。 Retire(pOld); } template <typename K,typename V> void UnLockMap<K,V>::Retire(Map* pOld) { // put it in the retired list rlist[rlistLength] = (int)pOld; //这里的R是一个门限,就是垃圾回收的门限 if (rlistLength >= R) { Scan(); } } //目前为止我们有两个容器,一个是公共的HPRecType,这个是装每个线程用到的无锁结构 //一个容器是Rlist这个vector,这是每个线程的独立私有变量,存放每个线程需要释放的无锁结构 //我们还需要一个操作就是Scan,这个函数的作用就是在HPRecType查找是否有Rlist种的数据, //如果Rlist中需要释放的结构在HPRecType中没找到就代表这个时候没有线程使用这个结构,所以 //代表是安全的。我们可以释放! template <typename K,typename V> void UnLockMap<K,V>::Scan() { // Stage 1: Scan hazard pointers list // collecting all non-null ptrs //下面是定义一个临时容器存储HPRecType的pHazard_ //如果pHazard_ = 0 则不放入容器 std::vector<void*> hp; Point* head = hpRType.Head(); while (head) { void * p = head->pHazard_; if (p) hp.push_back(p); head = head->pNext_; } // Stage 2: sort the hazard pointers //将hp容器进行排序 sort(hp.begin(), hp.end(),std::less<void*>()); // Stage 3: Search for'em! for(int i=0;i<rlistLength;++i) { //进行二分法查找,如果*i在hp中没找到就代表这个结构没有人用。 if (!binary_search(hp.begin(),hp.end(),(void*)rlist[i])) { // Aha! //没有人用就可以删除 delete (Map*)rlist[i]; //下面两句是一个小技巧,就是当在rlist中删除了某个 //结构的时候将rlist最后一个元素填入删除位置。 if (i != rlistLength - 1) { rlist[i] = rlist[rlistLength - 1]; --rlistLength; } } } } void autotest() { UnLockMap<int,char> map; map.Update(0,'A'); map.Update(1,'B'); map.Update(2,'C'); map.Update(3,'D'); map.Update(4,'E'); std::cout<<map.Get(0)<<std::endl; std::cout<<map.Get(1)<<std::endl; std::cout<<map.Get(2)<<std::endl; std::cout<<map.Get(3)<<std::endl; std::cout<<map.Get(4)<<std::endl; } }//namespace azure
代码经过我调试,可以通过VS和g++ 。
相关文章推荐
- 锁无关的(Lock-Free)数据结构——在避免死锁的同时确保线程继续
- 实现一个lockfree的队列——错误修改
- 实现一个lockfree 的队列
- 无锁(lock-free)队列的一个简单实现
- 锁无关的(Lock-Free)数据结构(二)
- 锁无关的(Lock-Free)数据结构——在避免死锁的同时确保线程继续
- 锁无关的(Lock-Free)数据结构——在避免死锁的同时确保线程继续 ZZ
- 锁无关的(Lock-Free)数据结构
- [转载]锁无关的(Lock-Free)数据结构
- 锁无关的(Lock-Free)数据结构——在避免死锁的同时确保线程继续
- 锁无关的(Lock-Free)数据结构——在避免死锁的同时确保线程继续
- 锁无关的(Lock-Free)数据结构——在避免死锁的同时确保线程继续
- 锁无关的(Lock-Free)数据结构——在避免死锁的同时确保线程继续
- 锁无关的(Lock-Free)数据结构(一)
- 锁无关的(Lock-Free)数据结构
- 利用链表实现一个先入后出的栈结构,并提供栈操作的push和pop的接口
- 无锁(lock-free)数据结构
- [PHP][Function]实现将一个文件夹下的所有文件及文件夹复制到另一个文件夹里(保持原有结构)[已验证][原创]
- 数据结构与算法_两个栈实现一个队列的功能
- java实现以树结构打印一个文件目录结构