您的位置:首页 > 其它

实现一个锁无关(lock free)结构~

2009-12-31 16:35 375 查看
我本是菜鸟,但是总是对一些新技术抱着好奇的心态想去研究一下。

前段时间看了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++ 。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: