对于典型的无锁结构实现,一般有两方面的问题需要解决
无锁算法的垃圾回收和数据结构和应用场景关联较弱,一般会被单独作为一个课题考虑,例如Performance of memory reclamation for lockless synchronization这篇文章对于一些典型的无锁回收进行了综述评价;其中提到了典型的几个回收方案QSBR(Quiescent-state-based reclamation)、EBR(Epoch-based reclamation)、HPBR(Hazard-pointer-based reclamation)
Epoch是EBR机制的一个修改版实现,核心是通过分拆访问、淘汰和回收动作降低机制引起的额外开销,主要区别点有
#include "babylon/concurrent/epoch.h"
using ::babylon::Epoch;
// 定义一个Epoch实例
Epoch epoch;
// 访问共享结构之前使用Epoch开启一个线程局部的临界区
{
std::lock_guard<Epoch> lock {epoch};
... // 临界区内部可以访问共享结构的元素
} // 临界区结束后,得到的元素指针就不再能被使用了
// 除了线程局部模式,也可以主动创建Accessor跟踪临界区
auto accessor = epoch.create_accessor();
{
std::lock_guard<Accessor> lock {accessor};
... // 临界区内部可以访问共享结构的元素
} // 临界区结束后,得到的元素指针就不再能被使用了
{
accessor.lock();
Task task {::std::move(accessor), ...} // 可以通过移动accessor转移临界区
... // 可以跟随task转移到异步线程等,再最终accessor.unlock()后结束临界区
} // 转移走的话,临界区就不会在此结束了
// 淘汰操作
... // 操作共享结构,摘除一些元素
// 推进epoch,返回之前摘除元素可被回收的最小epoch
auto lowest_epoch = epoch.tick();
// lowest_epoch和摘除的元素绑定,后续可以同步或者异步检测进行真正的回收
auto low_water_mark = epoch.low_water_mark();
// 当low_water_mark超过lowest_epoch后,元素即可以安全回收
if (lowest_epoch <= low_water_mark) {
... // 可以回收lowest_epoch对应的元素
}