这里主要实现了多线程环境下的skiplist,读操作(count, find, skipper)都是lock free的,写操作(remove, add)也只是小范围的加了锁
主要的用法如下:
Sample usage:
typedef ConcurrentSkipListSkipListT;
shared_ptrsl(SkipListT::createInstance(init_head_height);
{
// It's usually good practice to hold anaccessor only during
// its necessary life cycle (but not ina tight loop as
// Accessor creation incurs ref-countingoverhead).
//
// Holding it longer delaysgarbage-collecting the deleted
// nodes in the list.
// Accessor提供了访问skip list的接口,我们不能直接使用skip list对象来访问数据
SkipListT::Accessor accessor(sl);
accessor.insert(23); // 增加节点
accessor.erase(2); // 删除节点
for (auto &elem : accessor) {
// use elem to access data
}
... ...
}
还有一种访问方式是Skipper,主要是用来跳过一部分数据,例如
{
SkipListT::Accessor accessor(sl);
SkipListT::Skipper skipper(accessor);
skipper.to(30); // 跳到比30大的第一个节点
if (skipper) {
CHECK_LE(30, *skipper);
}
... ...
// GC may happen when the accessor getsdestructed.
}
我们这里重点从Accessor来分析一下查找、增加和删除的流程:
查找:
typedef detail::csl_iterator
iterator find(const key_type &value);
std::pairfindNodeDownRight(const value_type &data) const {
NodeType *pred = head_.load(std::memory_order_consume); // 从head开始查找
int ht = pred->height();
NodeType *node = nullptr;
bool found = false;
while (!found) {
// stepping down,直到找到一个节点pred的数据比data大
for (; ht > 0 && less(data,pred->skip(ht - 1)); --ht) {}
if (ht == 0) returnstd::make_pair(pred->skip(0), 0); //not found
node = pred->skip(--ht); // node <= data now
// stepping right,继续接近data
while (greater(data, node)) {
pred = node;
node = node->skip(ht);
}
found = !less(data, node);
}
return std::make_pair(node, found);
}
增加:
std::pair
std::pair addOrGetData(const value_type &data) {
NodeType *preds[MAX_HEIGHT],*succs[MAX_HEIGHT];
NodeType *newNode;
size_t newSize;
while (true) {
int max_layer = 0;
// 找到data对应的节点,以及它的前继和后继,max_layer返回当前skip list的最大层级
// 返回值layer是data对应的节点备找到时的layer
int layer =findInsertionPointGetMaxLayer(data, preds, succs, &max_layer);
if (layer >= 0) {// 如果找到
NodeType *nodeFound = succs[layer];
DCHECK(nodeFound != nullptr);
if (nodeFound->markedForRemoval()) {
continue; // if it's getting deleted retry findingnode.
}
// wait until fully linked. 可能节点被其他线程加入了,暂时还没有fully linked
// 等待完成后再返回给用户完整的节点
while(UNLIKELY(!nodeFound->fullyLinked())) {}
return std::make_pair(nodeFound, 0);
}
// need to capped at the original height-- the real height may have grown
// 按概率生成新的节点高度,新节点的高度上限设为max_layer+1
// 值得注意的是选取概率是1/e
int nodeHeight =detail::SkipListRandomHeight::instance()->
getHeight(max_layer + 1);
ScopedLocker guards[MAX_HEIGHT];
// 把前继全部加上锁
if (!lockNodesForChange(nodeHeight,guards, preds, succs)) {
continue; // give up the locks andretry until all valid
}
// locks acquired and all valid, need tomodify the links under the locks.
// 按照生成的高度建立新的节点
newNode = NodeType::create(nodeHeight,data);
// 把新的节点联入skip list中
for (int layer = 0; layer setSkip(layer,succs[layer]);
preds[layer]->setSkip(layer,newNode);
}
// 标记fully linked
newNode->setFullyLinked();
newSize = incrementSize(1);
break;
}
int hgt = height();
size_t sizeLimit =
detail::SkipListRandomHeight::instance()->getSizeLimit(hgt);
// 检查是否需要增加skip list节点的高度
if (hgt < MAX_HEIGHT && newSize> sizeLimit) {
growHeight(hgt + 1);
}
CHECK_GT(newSize, 0);
return std::make_pair(newNode, newSize);
}
boollockNodesForChange(int nodeHeight,
ScopedLocker guards[MAX_HEIGHT],
NodeType *preds[MAX_HEIGHT], // 插入或删除节点的前继
NodeType *succs[MAX_HEIGHT], // 插入或删除节点的后继
bool adding=true) {// adding为true表明该函数是在add里备调用,否则是在remove里被调用
NodeType *pred, *succ, *prevPred = nullptr;
bool valid = true;
for (int layer = 0; valid && layer< nodeHeight; ++layer) {
pred = preds[layer];
DCHECK(pred != nullptr) <<"layer=" << layer << " height=" <
实际调用的是skip list的remove方法
我们来分析一下remove的源码
bool remove(const value_type &data) {
NodeType *nodeToDelete = nullptr;
ScopedLocker nodeGuard;
bool isMarked = false; //表示是否已经完成对删除节点的标记(markForRemoval)
int nodeHeight = 0;
NodeType* preds[MAX_HEIGHT],*succs[MAX_HEIGHT];
while (true) {
int max_layer = 0;
// 先找到要删除的节点,以及它的前继和后继,max_layer返回当前skiplist的最大层级
// 返回值layer是data对应的节点备找到时的layer
int layer =findInsertionPointGetMaxLayer(data, preds, succs, &max_layer);
// okToDelete的判断条件是:节点fully linked,节点没有被标记markedForRemoval,
// 并且节点的top layer和查到节点的layer一致,否则说明该节点是partailly linked
if (!isMarked && (layer < 0 ||!okToDelete(succs[layer], layer))) {
return false;
}
// 给要删除的节点设置markedForRemoval
if (!isMarked) {
nodeToDelete = succs[layer];
nodeHeight = nodeToDelete->height();
nodeGuard =nodeToDelete->acquireGuard();
if(nodeToDelete->markedForRemoval()) return false;
nodeToDelete->setMarkedForRemoval();
isMarked = true;
}
// acquire pred locks from bottom layerup
// 获取所有前继的锁
ScopedLocker guards[MAX_HEIGHT];
if (!lockNodesForChange(nodeHeight,guards, preds, succs, false)) {
continue; // this will unlock all the locks
}
// 修改前继指针,删除对应节点
for (int layer = nodeHeight - 1; layer>= 0; --layer) {
preds[layer]->setSkip(layer,nodeToDelete->skip(layer));
}
incrementSize(-1);// 这里不会降低高度
break;
}
// 把节点加入gc中
recycle(nodeToDelete);
return true;
}
- 这里值得提一下的是Recycler对象,他负责回收被删除的节点,但其实它只是把节点加入一个vector,然后在Recycler对象析构或者显示调用release方法时才会去释放这些节点