这里主要实现了多线程环境下的skiplist,读操作(count, find, skipper)都是lock free的,写操作(remove, add)也只是小范围的加了锁
主要的用法如下:
Sample usage:
typedef ConcurrentSkipList<int>SkipListT;
shared_ptr<SkipListT>sl(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<value_type, NodeType> iterator; // 利用boostiterator_facade生成的iterator
iterator find(const key_type &value);
- 调用skip list的find方法
- 调用findNode方法,如果找到节点并且该节点没有markedForRemoval的话就返回,否则返回nullptr
- 调用findNodeDownRight(查找时先向下遍历,然后再向右遍历)方法,这里值得说一下的是,skip list里还实现了一个findNodeRightDown(查找时先向右遍历,然后再向下遍历)方法,但性能不如findNodeDownRight,因为后者的cache locality要更好一些
- 我们来看下findNodeDownRight方法是怎么实现的:
std::pair<NodeType*, int>findNodeDownRight(const value_type &data) const {
NodeType *pred = head_.load(std::memory_order_consume); // 从head开始查找
int ht = pred->height();
NodeType *node = nu